集合(Collection)是一组用于存储和操作对象的数据结构。Java集合框架(Java Collections Framework, JCF)提供了一个统一的架构,用于表示和操作集合,它包含了一系列接口、实现类以及算法。
Collection接口
Collection接口是集合框架的根接口,它扩展了Iterable接口,定义了所有集合类型共有的方法。Collection接口本身并不提供直接的实现,而是作为其他更具体的集合接口(如List、Set和Queue)和类的超接口。以下是Collection接口的详细介绍:
List
List接口继承自Collection接口。List接口表示一个有序的集合,可以包含重复的元素,并且每个元素都有一个索引位置,可以通过这个索引来访问元素。
List接口的主要特点:
1. 有序性:List中的元素按照特定的顺序排列,这个顺序可以是元素插入的顺序,也可以是通过其他方式指定的顺序。
2. 可重复性:List允许包含重复的元素。
3. 有索引:List中的每个元素都可以通过索引来访问,索引从0开始。
void add(int index, E element) | 在指定索引处插入指定的元素 |
int indexOf(Object o) | 返回列表中第一次出现的指定元素的索引,如果列表不包含此元素,则返回-1 |
boolean addAll(Collection<? extends E> c) | 继承自Collection接口,将指定集合中的所有元素追加到列表的末尾 |
Object[] toArray() | 返回一个包含列表中所有元素的数组。 |
E remove(int index) | 移除列表中指定位置的元素 |
<T> T[] toArray(T[] a) | 返回一个包含列表中所有元素的数组,其运行时类型是指定数组的运行时类型。 |
E set(int index, E element) | 用指定的元素替换列表中指定位置的元素。 |
List接口的实现类:
ArrayList
ArrayList是基于动态实现的数组,当你使用无参构造函数ArrayList()创建一个空ArrayList对象时,它的底层数组长度为0,当你添加第一个元素是,数组容量会变为10,当数组容量不够时,其会将当前容量增加1.5倍,这个过程被称为扩容
如何创建ArrayList
ArrayList<String> list = new ArrayList<>();
一些方法的实例:
如果删除的不是最后一个元素,ArrayList 会将删除位置之后的元素向前移动,以填补空出来的位置。
ArrayList优点:实现了可调整大小的数组。允许包含重复的元素和null值。它是非同步的,适用于查找和更新操作。
LinkedList
LinkedList和ArrayList一样实现了List接口,但它在内部数据结构和性能存和ArrayList存在一些差异,ArrayList是基于动态数组实现的,而LinkedList是基于双向链表实现的,每个元素都包含数据和两个指针,分别指向前一个和后一个元素。
就像这样:
插入前 插入后
双向链表在任何位置添加或删除元素都非常高效,因为只需要改变指针,时间复杂度为 O(1)。
创建LinkedList实例:
LinkedList<Integer> list = new LinkedList<>();
一些方法的示例:
ArrayList适用于需要频繁随机访问元素的场景和元素数量相对稳定,不需要频繁插入和删除的场景。
LinkedList适用于需要频繁插入和删除元素的场景和不需要频繁随机访问元素的场景。
*Iterator接口(不属于Collection)
Iterator接口是集合框架的一部分,它提供了一种遍历集合(例如 List、Set或 Map 的集合视图)中元素的方法,而不需要暴露其底层表示。Iterator接口位于 java.util包中。
boolean hasNext():如果迭代器在遍历集合的过程中还有更多的元素,则返回 true,如果所有元素都已经遍历完毕,则返回 false。
next():返回迭代器中的下一个元素。
remove():从迭代器指向的集合中移除迭代器返回的最后一个元素。每次调用 next()后只能调用一次 remove(),否则会抛出异常。
Iterator例子:
public class Main {
public static void main(String[] args) {
// 创建一个 List
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.add("Cherry");
// 获取 Iterator
Iterator<String> iterator = list.iterator();
// 使用 Iterator 遍历 List
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
// 如果需要,可以移除元素
if (fruit.equals("Banana")) {
iterator.remove();
}
}
// 打印修改后的 List
System.out.println(list);
}
}
在这个示例中,我们创建了一个 ArrayList,并使用 iterator() 方法获取了一个 Iterator。然后,我们使用 hasNext() 和 next() 方法遍历列表中的每个元素,并打印出来。如果元素是 Banana,我们使用 remove() 方法将其从列表中移除。
Iterator接口是 Java 集合框架中的基础,它为遍历集合提供了一个通用的机制。
*foreach循环
Java 5 引入了 for-each 循环,它在内部使用了 Iterator,从而简化了集合的遍历。
foreach 循环的基本语法:
for (声明语句 : 表达式) {
// 循环体
}
声明语句:用于声明一个局部变量,该变量的类型必须与数组或集合中元素的类型兼容。在每次迭代中,foreach 循环会将数组或集合中的下一个元素赋值给这个变量。
表达式:是一个数组或实现了 Iterable 接口的集合。
下面是使用 foreach循环遍历数组和集合的示例:
遍历数组:
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println(number);
}
遍历集合:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) {
System.out.println(name);
}
特点:
简洁:不需要显式地创建迭代器或处理索引。
安全:不会出现数组越界异常,因为 foreach 循环会自动处理边界。
不可变:在foreach 循环中不能直接修改集合或数组的内容,因为它每次迭代都只是获取元素的副本。如果需要修改元素,需要使用普通的 for 循环或迭代器。
foreach 循环的引入大大简化了遍历集合和数组的代码,使其更加直观和易于维护。
Set
Set 接口是 Java 集合框架中的一个根接口,它继承自 Collection 接口。Set 接口的特点是其元素是无序的,并且不包含任何重复的元素。
Set接口主要有两个实现类,分别是HashSet和TreeSet。
HashSet
HashSet的特点:
唯一性:HashSet 不允许存储重复的元素。
无序性:HashSet不保证元素的顺序,即元素插入和遍历的顺序可能不同。
基于 HashMap 实现:HashSet 的实现是基于 HashMap,每个元素实际上是 HashMap 的一个键。
创建一个HashSet:
HashSet<String> set = new HashSet<>();
主要方法:
add(E e):添加元素到 HashSet中。
(Object o):从HashSet中移除指定的元素。
contains(Object o):检查 HashSet是否包含指定的元素。
size():返回 HashSet中的元素数量。
clear():移除 HashSet中的所有元素。
下面是使用hashset的实例:
public class Example {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("Apple");
set.add("Banana");
set.add("Cherry");
// 重复添加不会成功
set.add("Apple");
System.out.println(set); // 输出可能为 [Apple, Banana, Cherry] 或其他顺序
System.out.println(set.contains("Banana")); // 输出 true
System.out.println(set.size()); // 输出 3
}
}
当你需要存储一组唯一元素,并且对元素的顺序没有要求时。
当你需要快速检索元素是否存在时,因为 HashSet的查找效率很高。
(在这些场景下使用HashSet比较好)
TreeSet
TreeSet是 Java 集合框架中的一个类,它实现了 NavigableSet接口,并且底层是基于红黑树实现的。TreeSet 用于存储一组有序的元素,以下是 TreeSet的详细介绍:
*红黑树:红黑树确保没有一条路径会比其他路径长出两倍,因而是近似平衡的,从而保证了树的高度大约是 log(n),其中 n 是树中节点的数量。这使得红黑树的查找、插入和删除操作的时间复杂度最坏情况下为 O(log n)。
特点:
1. 有序集合:TreeSet 中的元素按照自然顺序或者指定的比较器进行排序。这意味着元素是以升序排列的。
2. 唯一性:与 HashSet一样,TreeSet 不允许存储重复的元素。
方法:
add(E e):添加元素到 TreeSet 中。
remove(Object o):从 TreeSet 中移除指定的元素。
contains(Object o):检查 TreeSet是否包含指定的元素。
size():返回 TreeSet 中的元素数量。
clear():移除 TreeSet 中的所有元素。
isEmpty():检查 TreeSet 是否为空。
first():返回 TreeSet 中最小的元素。
last():返回 TreeSet中最大的元素。
示例:
public class Example {
public static void main(String[] args) {
TreeSet<Integer> set = new TreeSet<>();
set.add(10);
set.add(5);
set.add(20);
// 重复添加不会成功
set.add(10);
System.out.println(set); // 输出 [5, 10, 20]
System.out.println(set.first()); // 输出 5
System.out.println(set.last()); // 输出 20
System.out.println(set.size()); // 输出 3
// 查找并移除元素
set.remove(10);
System.out.println(set); // 输出 [5, 20]
}
}
使用场景:
需要存储一组唯一且有序的元素时。
当你需要按照元素的自然顺序或者自定义顺序遍历元素时。
当你需要高效地进行范围查询时。
TreeSet 的操作通常比 HashSet 的操作要慢,因为红黑树的操作复杂度是 O(log n),而 HashSet 的操作复杂度通常是 O(1)。
Map
map 是一种抽象数据类型,用于存储键值对。map 接口定义了一系列操作,这些操作允许用户根据键来存储、检索、更新和删除元素。
键(Key):每个键是唯一的,用于在map中标识一个值。
值(Value):与键相关联的数据。
键值对:键和值的组合,是map中存储的基本单元。
特点
键的唯一性:在 map 中,每个键只能对应一个值,因此键必须是唯一的。
无序性:标准的 map接口不保证元素的顺序。不过,有些实现提供了有序 map,如基于红黑树或平衡二叉搜索树的实现。
键的比较:map 通常要求键类型是可以比较的,以便能够确定键的唯一性。
HashMap
HashMap是基于哈希表实现的映射接口的一个实现,它存储键值对,并允许快速检索。HashMap使用加载因子来确定何时进行扩容。当哈希桶中的元素数量达到当前容量与加载因子的乘积时,HashMap会进行扩容操作,通常是扩大为原来容量的两倍。
特点:
动态调整大小:HashMap 能够根据存储的元素数量动态调整其内部数组的大小,这个过程称为rehashing。
HashMap():创建一个默认初始容量(16)和默认加载因子(0.75)的空 HashMap。
HashMap(int initialCapacity):创建一个指定初始容量的空 HashMap。
创建一个HashMap:
HashMap<String, Integer> map = new HashMap<>();
常用方法:
put(K key, V value):将指定的键值对插入到 HashMap 中。
get(Object key):返回指定键所映射的值,如果找不到则返回 null。
remove(Object key):移除键对应的键值对,并返回被移除的值。
containsKey(Object key):检查 HashMap是否包含指定的键。
containsValue(Object value):检查 HashMap是否包含指定的值。
size():返回 HashMap中键值对的数量。
示例:
public class HashMapExample {
public static void main(String[] args) {
HashMap<String, Integer> map = new HashMap<>();
// 插入键值对
map.put("Apple", 1);
map.put("Banana", 2);
// 获取值
int value = map.get("Apple"); // 返回 1
// 删除键值对
map.remove("Banana");
// 检查键是否存在
boolean contains = map.containsKey("Apple"); // 返回 true
// 获取大小
int size = map.size(); // 返回 1
// 清空 HashMap
map.clear();
}
}
插入、删除和检索操作的平均时间复杂度是 O(1),在最坏情况下会退化为 O(n)。
*如果哈希桶的索引位置上有多个键值对,这些键值对将以链表的形式存储。在 Java 8 及以上版本中,当链表长度超过一定阈值时,链表会转换为红黑树,以优化查找性能。
TreeMap
TreeMap是 Java 中的一个集合类,它实现了 SortedMap接口,可以用来存储键值对,并且可以根据键的自然顺序或者通过构造器传入的 Comparator来排序。
特点:底层使用红黑树数据结构,保证了元素的有序性和较高的操作效率。
TreeMap():创建一个基于键的自然排序的空 TreeMap。
TreeMap(Comparator<? super K> comparator):创建一个根据指定 Comparator 排序的空 TreeMap。
创建一个TreeMap:
TreeMap<String, Integer> map = new TreeMap<>();
常用方法:
put(K key, V value):将指定的键值对插入到 TreeMap 中。
get(Object key):返回指定键所映射的值,如果找不到则返回 null。
remove(Object key):移除键对应的键值对,并返回被移除的值。
containsKey(Object key):检查 TreeMap 是否包含指定的键。
containsValue(Object value):检查 TreeMap是否包含指定的值。
firstKey():返回 TreeMap 中第一个(最低)键。
lastKey():返回 TreeMap 中最后一个(最高)键。
示例:
public class TreeMapExample {
public static void main(String[] args) {
TreeMap<String, Integer> map = new TreeMap<>();
// 插入键值对
map.put("Apple", 1);
map.put("Banana", 2);
map.put("Cherry", 3);
// 获取值
int value = map.get("Apple"); // 返回 1
// 删除键值对
map.remove("Banana");
// 检查键是否存在
boolean contains = map.containsKey("Apple"); // 返回 true
// 获取第一个键
String firstKey = map.firstKey(); // 返回 "Apple"
// 获取最后一个键
String lastKey = map.lastKey(); // 返回 "Cherry"
// 遍历 TreeMap
for (String key : map.keySet()) {
System.out.println(key + " -> " + map.get(key));
}
}
}
TreeMap维护了元素的顺序,所以在需要对键进行排序的场景下,TreeMap是一个很好的选择。
Lambda
Lambda 表达式是 Java 8 中引入的一个重要特性,它提供了一种简洁的语法来表示匿名函数,允许开发者更加灵活地处理函数式接口(即只包含一个抽象方法的接口)
Lambda 表达式的语法基本形式如下:
(parameters) -> expression
或
(parameters) ->{ statements; }
parameters
是参数列表,expression
或 { statements }
是Lambda 表达式的主体。如果只有一个参数,可以省略括号;如果没有参数,也需要空括号。
如果参数类型可以由编译器推断出来,那么参数的类型也可以省略:
(parameter) -> { body; }
如果只有一个参数,并且它的类型可以被推断出来,那么圆括号也可以省略:
parameter -> { body; }
Lambda 表达式可以访问以下作用域内的变量:
局部变量:Lambda 表达式可以访问局部变量,但这些变量必须是最终变量(即必须是不可变的)。
成员变量:Lambda 表达式可以访问或修改外围类的成员变量。
静态变量:Lambda 表达式可以访问静态变量。
方法引用
有时候 Lambda 表达式只是调用一个已存在的方法,在这种情况下,可以使用方法引用来进一步简化代码。方法引用的语法是:
Class::methodName
例如:
List<String> names = Arrays.asList("a", "B", "C");
names.forEach(System.out::println);
Lambda 表达式是 Java 8 函数式编程的基础,它简化了代码,并使 Java 更具表现力。
持续更新中
标签:map,set,java,HashMap,HashSet,元素,接口,2024,集合 From: https://blog.csdn.net/2301_81490350/article/details/143187885