Java Collection API
概要
Java Collections Framework(Java集合框架)是Java语言中的一组类和接口,提供了在Java中处理对象集合的基础设施。集合是通常作为一个单一单元存储、操作和处理的对象组。Java集合框架通过提供用于处理集合的通用和一致的API来简化集合的处理。
以下是Java集合API中的一些关键组件和概念:
- 接口:
Collection
(集合):这是Java集合框架的根接口,它表示一组对象并定义了添加、删除和迭代元素等基本操作。List
(列表):有序元素的集合,允许重复。Set
(集):不重复元素的无序集合。Map
(映射):键值对的集合。
- 类:
ArrayList
(数组列表):使用动态数组实现List接口。LinkedList
(链表):使用双向链表实现List接口。HashSet
(哈希集):使用哈希表存储实现Set接口。TreeSet
(树集):使用排序树结构实现Set接口。HashMap
(哈希映射):使用哈希表实现Map接口。TreeMap
(树映射):使用排序树结构实现Map接口。
- 迭代器:该框架提供了迭代器,用于遍历集合中的元素。一些常见的迭代器接口包括
Iterator
(迭代器)、ListIterator
(列表迭代器)和SetIterator
(集合迭代器)。 - 泛型类型:大多数集合类和接口都是泛型的,这意味着您可以指定它们持有的对象类型。例如,
ArrayList<Integer>
只能保存整数。 - 算法:该框架在
Collections
类中提供了一组静态方法,用于在集合上执行操作,如排序、搜索、洗牌等。 - 比较器:
Comparator
接口允许您为集合中的对象定义自定义排序规则。
List
List
是 Java 集合框架中的一个接口,表示有序元素的集合,允许元素重复。以下是如何使用 List
接口的一些常见操作:
ArrayList
-
创建一个 List 对象:
可以使用各种类来实现
List
接口,例如ArrayList
、LinkedList
。下面是一个使用ArrayList
的示例:List<String> myList = new ArrayList<>();
-
添加元素:
使用
add
方法可以向列表中添加元素。myList.add("苹果"); myList.add("香蕉"); myList.add("樱桃");
-
访问元素:
使用
get
方法可以按索引访问列表中的元素。String fruit = myList.get(0); // 获取第一个元素,索引从0开始
-
修改元素:
通过索引可以修改列表中的元素。
myList.set(1, "葡萄"); // 将索引为1的元素修改为"葡萄"
-
删除元素:
使用
remove
方法可以删除列表中的元素。myList.remove(2); // 删除索引为2的元素("樱桃")
-
查找元素:
可以使用
contains
方法检查列表中是否包含特定元素。boolean containsBanana = myList.contains("香蕉");
-
获取列表大小:
使用
size
方法可以获取列表中元素的数量。int size = myList.size();
-
迭代列表:
使用循环或迭代器可以遍历列表中的元素。
for (String fruit : myList) { System.out.println(fruit); }
或者使用迭代器:
Iterator<String> iterator = myList.iterator(); while (iterator.hasNext()) { String fruit = iterator.next(); System.out.println(fruit); }
-
排序列表:
使用
Collections.sort()
方法可以对列表进行排序。Collections.sort(myList); // 对列表进行升序排序
LinkedList
LinkedList
是 Java 集合框架中的一个实现了 List
接口的类,它基于双向链表数据结构。以下是如何使用 LinkedList
的一些常见操作:
-
创建一个 LinkedList 对象:
可以使用
LinkedList
类来创建一个新的链表:LinkedList<String> linkedList = new LinkedList<>();
-
添加元素:
使用
add
方法可以向链表尾部添加元素:linkedList.add("苹果"); linkedList.add("香蕉"); linkedList.add("樱桃");
还可以使用
addFirst
和addLast
方法在链表的开头和结尾添加元素:linkedList.addFirst("葡萄"); linkedList.addLast("桃子");
-
访问元素:
使用
get
方法可以按索引访问链表中的元素:String fruit = linkedList.get(0); // 获取第一个元素,索引从0开始
-
修改元素:
通过索引可以修改链表中的元素:
linkedList.set(1, "柚子"); // 将索引为1的元素修改为"柚子"
-
删除元素:
使用
remove
方法可以删除链表中的元素。可以按索引删除,也可以按元素内容删除:linkedList.remove(2); // 删除索引为2的元素("樱桃") linkedList.remove("桃子"); // 删除元素内容为"桃子"的元素
-
获取链表大小:
使用
size
方法可以获取链表中元素的数量:int size = linkedList.size();
-
迭代链表:
使用循环或迭代器可以遍历链表中的元素,也可以使用
getFirst
和getLast
方法获取链表的第一个和最后一个元素:for (String fruit : linkedList) { System.out.println(fruit); } String firstFruit = linkedList.getFirst(); String lastFruit = linkedList.getLast();
-
在链表头部和尾部操作元素:
LinkedList
提供了addFirst
、addLast
、getFirst
和getLast
等方法,以便在链表的头部和尾部执行操作。linkedList.addFirst("草莓"); linkedList.addLast("橙子"); String firstFruit = linkedList.getFirst(); String lastFruit = linkedList.getLast();
这些是一些常见的 LinkedList
操作。由于 LinkedList
是基于链表的,因此它在插入和删除操作方面可以比 ArrayList
更高效,但在随机访问方面性能可能较差。您可以根据具体需求选择合适的实现类。
二者比较
ArrayList
和 LinkedList
都是 Java 集合框架中的列表(List)实现,用于存储有序的元素。它们有不同的内部数据结构和性能特点,适用于不同的使用场景。
ArrayList
- 内部数据结构:
ArrayList
基于动态数组实现。它使用数组来存储元素,可以根据需要自动增长或收缩数组的大小。 - 特点:
- 随机访问快:由于使用数组,
ArrayList
支持常数时间内的随机访问(通过索引访问元素)。 - 插入和删除较慢:在中间插入或删除元素时,需要移动其他元素,因此性能较差。
- 随机访问快:由于使用数组,
- 适用场景:
- 当需要快速随机访问元素,而不需要频繁的插入和删除操作时,
ArrayList
是一个不错的选择。
- 当需要快速随机访问元素,而不需要频繁的插入和删除操作时,
LinkedList
- 内部数据结构:
LinkedList
基于双向链表实现。每个元素都包含对前一个和后一个元素的引用。 - 特点:
- 随机访问较慢:由于需要从头或尾遍历链表来查找元素,随机访问的性能较差。
- 插入和删除快:在中间插入或删除元素时,只需更新相邻元素的引用,因此性能较好。
- 适用场景:
- 当需要频繁插入和删除元素,而不需要频繁随机访问时,
LinkedList
是一个更合适的选择。 - 另外,
LinkedList
适合在双向遍历(向前和向后)时使用。
- 当需要频繁插入和删除元素,而不需要频繁随机访问时,
总结
- 如果您的应用程序需要快速随机访问元素,并且插入和删除操作相对较少,
ArrayList
可能是更好的选择,因为它具有更好的随机访问性能。 - 如果您的应用程序需要频繁进行插入和删除操作,尤其是在中间插入和删除元素时,
LinkedList
可能更适合,因为它具有更好的插入和删除性能。 - 您还可以根据具体需求和性能要求选择合适的数据结构。有时候,混合使用这两种数据结构也是一种有效的策略。
Iterator
迭代器(Iterator)是 Java 集合框架中的一个重要概念,它是用于遍历(迭代)集合中的元素的一种设计模式。迭代器提供了一种标准的方式来访问集合的元素,而不需要了解集合的内部结构。迭代器常用于循环遍历集合,无论集合的具体实现是数组、链表还是其他数据结构。
在 Java 中,几乎所有的集合类(如 List、Set、Map)都实现了迭代器。以下是迭代器的常见用法和方法:
-
获取迭代器:
- 使用
iterator()
方法获取集合的迭代器。 - 对于 List 类型的集合,还可以使用
listIterator()
方法获取一个列表迭代器,它允许双向遍历并在遍历过程中修改元素。
List<String> myList = new ArrayList<>(); Iterator<String> iterator = myList.iterator();
- 使用
-
遍历集合:
- 使用
hasNext()
方法检查是否还有下一个元素。 - 使用
next()
方法获取下一个元素。
while (iterator.hasNext()) { String element = iterator.next(); // 处理 element }
- 使用
-
删除元素:
- 使用迭代器的
remove()
方法可以安全地删除集合中的元素,而不会引发并发修改异常。
iterator.remove(); // 删除当前元素
- 使用迭代器的
-
ListIterator(列表迭代器):
- 对于 List 类型的集合,可以使用
listIterator()
方法获取列表迭代器,它允许双向遍历,并可以在遍历过程中修改元素。 ListIterator
提供了额外的方法,如previous()
和set()
,用于在遍历中访问前一个元素和修改当前元素。
ListIterator<String> listIterator = myList.listIterator(); while (listIterator.hasNext()) { String element = listIterator.next(); // 处理 element }
- 对于 List 类型的集合,可以使用
迭代器是一种非常有用的工具,因为它提供了一种一致的方式来遍历各种不同类型的集合,而不必关心它们的内部实现。迭代器也能够在遍历过程中安全地进行元素删除操作,这对于修改集合而不破坏遍历的情况非常有帮助。
Set
在Java中,Set
是一个接口,表示一种集合,其中不允许重复的元素。Set
集合通常用于存储一组唯一的元素。Java提供了多个实现 Set
接口的类,包括 HashSet
、LinkedHashSet
和 TreeSet
。下面是如何使用 Set
集合的基本操作:
-
创建一个 Set 对象:
您可以使用不同的实现类来创建
Set
集合,例如HashSet
、LinkedHashSet
或TreeSet
。javaCopy code Set<String> mySet = new HashSet<>(); // 创建一个 HashSet
-
添加元素:
使用
add
方法将元素添加到集合中。请注意,集合不会允许重复的元素。mySet.add("苹果"); mySet.add("香蕉"); mySet.add("樱桃");
-
检查元素是否存在:
使用
contains
方法来检查集合中是否包含特定元素。boolean containsBanana = mySet.contains("香蕉");
-
删除元素:
使用
remove
方法从集合中删除元素。mySet.remove("樱桃");
-
获取集合大小:
使用
size
方法获取集合中的元素数量。int size = mySet.size();
-
遍历集合:
您可以使用迭代器(Iterator)来遍历
Set
集合中的元素。Iterator<String> iterator = mySet.iterator(); while (iterator.hasNext()) { String element = iterator.next(); System.out.println(element); }
-
清空集合:
使用
clear
方法可以清空集合中的所有元素。mySet.clear();
-
不允许重复元素:
Set
集合不允许重复的元素。如果尝试添加一个已经存在的元素,它将被忽略。mySet.add("苹果"); mySet.add("苹果"); // 这个操作不会添加重复的 "苹果"
Set
集合非常适合用于存储一组唯一的元素,以及在元素存在性检查方面具有高效性能。根据具体需求,您可以选择不同的 Set
实现,例如 HashSet
提供了快速查找,LinkedHashSet
保持元素的插入顺序,而 TreeSet
则保持元素的有序性。
三种集合的对比
HashSet
, LinkedHashSet
, 和 TreeSet
都是 Java 集合框架中的 Set
接口的实现,用于存储一组唯一的元素。它们之间的区别主要在于内部数据结构和性能特点:
-
HashSet:
- 内部数据结构:
HashSet
基于哈希表实现,使用哈希函数来存储和检索元素。 - 无序性:
HashSet
不保证元素的顺序,元素的存储顺序是不确定的。 - 查找性能:
HashSet
具有快速的查找性能,平均情况下,查找操作的时间复杂度为 O(1)。 - 不允许重复元素:
HashSet
不允许重复的元素。
Set<String> hashSet = new HashSet<>();
- 内部数据结构:
-
LinkedHashSet:
- 内部数据结构:
LinkedHashSet
基于哈希表和链表实现,保持元素的插入顺序。 - 有序性:
LinkedHashSet
保持元素的插入顺序,因此迭代时元素的顺序与插入顺序相同。 - 查找性能:与
HashSet
相似,具有快速的查找性能。 - 不允许重复元素:
LinkedHashSet
不允许重复的元素。
Set<String> linkedHashSet = new LinkedHashSet<>();
- 内部数据结构:
-
TreeSet:
- 内部数据结构:
TreeSet
基于红黑树(一种自平衡二叉搜索树)实现,保持元素的有序性。 - 有序性:
TreeSet
保持元素的有序性,元素会按照它们的自然顺序或自定义比较器的顺序进行排序。 - 查找性能:相对于哈希表实现,
TreeSet
的查找性能略慢,平均情况下,查找操作的时间复杂度为 O(log n)。 - 不允许重复元素:
TreeSet
不允许重复的元素。
Set<String> treeSet = new TreeSet<>();
- 内部数据结构:
选择哪种实现取决于需求:
- 如果需要一个无序的集合,主要关注元素的查找性能,
HashSet
可能是最佳选择。 - 如果需要一个有序的集合,但仍希望具有快速查找性能,
LinkedHashSet
可能是更合适的,因为它保持了元素的插入顺序。 - 如果需要一个有序的集合,并且对元素的顺序有严格的要求,
TreeSet
是一个好选择,因为它可以按照自然顺序或自定义比较器的顺序进行排序。
无论选择哪个实现,Set
接口确保您将只存储唯一的元素。
Map
Map
是 Java 集合框架中的一个接口,用于存储键值对(key-value pairs)。每个键映射到一个值,因此 Map
允许您通过键来查找和检索值。Map
接口的常见实现包括 HashMap
、LinkedHashMap
、TreeMap
和 HashTable
等。以下是关于如何使用 Map
的基本操作:
-
创建一个 Map 对象:
您可以使用不同的实现类来创建
Map
集合,例如HashMap
、LinkedHashMap
、TreeMap
。Map<String, Integer> myMap = new HashMap<>();
-
添加键值对:
使用
put
方法将键值对添加到Map
中。myMap.put("苹果", 10); myMap.put("香蕉", 5); myMap.put("樱桃", 20);
-
获取值:
使用
get
方法通过键获取值。int appleCount = myMap.get("苹果");
-
检查键是否存在:
使用
containsKey
方法来检查键是否存在于Map
中。boolean containsBanana = myMap.containsKey("香蕉");
-
删除键值对:
使用
remove
方法从Map
中删除键值对。myMap.remove("樱桃");
-
获取键集合和值集合:
使用
keySet()
方法可以获取所有键的集合,使用values()
方法可以获取所有值的集合。Set<String> keys = myMap.keySet(); Collection<Integer> values = myMap.values();
-
遍历 Map:
使用迭代器或增强型
for
循环遍历Map
中的键值对。for (Map.Entry<String, Integer> entry : myMap.entrySet()) { String key = entry.getKey(); int value = entry.getValue(); System.out.println(key + ": " + value); }
-
清空 Map:
使用
clear
方法可以清空Map
中的所有键值对。myMap.clear();
Map
是一种非常有用的数据结构,它将值与唯一的键相关联,以便快速检索和更新数据。不同的 Map
实现有不同的性能特点和用途,根据需求,可以选择适当的实现。
HashMap、LinkedHashMap、TreeMap和HashTable的区别
HashMap
, LinkedHashMap
, TreeMap
, 和 Hashtable
都是 Java 中的 Map
接口的实现,用于存储键值对。它们有不同的性能特点和用途,以下是它们的主要区别:
-
HashMap:
- 内部数据结构:
HashMap
基于哈希表实现,使用哈希函数将键映射到值。 - 无序性:
HashMap
不保证键值对的顺序,元素的存储顺序是不确定的。 - 查找性能:
HashMap
具有快速的查找性能,平均情况下,查找操作的时间复杂度为 O(1)。 - 允许空键和值:
HashMap
允许存储空键和空值。
Map<String, Integer> hashMap = new HashMap<>();
- 内部数据结构:
-
LinkedHashMap:
- 内部数据结构:
LinkedHashMap
基于哈希表和双向链表实现,保持键值对的插入顺序。 - 有序性:
LinkedHashMap
保持键值对的插入顺序,因此迭代时键值对的顺序与插入顺序相同。 - 查找性能:与
HashMap
相似,具有快速的查找性能。 - 允许空键和值:
LinkedHashMap
允许存储空键和空值。
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
- 内部数据结构:
-
TreeMap:
- 内部数据结构:
TreeMap
基于红黑树(一种自平衡二叉搜索树)实现,保持键值对的有序性。 - 有序性:
TreeMap
保持键值对的有序性,元素会按照它们的自然顺序或自定义比较器的顺序进行排序。 - 查找性能:相对于哈希表实现,
TreeMap
的查找性能略慢,平均情况下,查找操作的时间复杂度为 O(log n)。 - 不允许空键:
TreeMap
不允许存储空键,但允许存储空值。
Map<String, Integer> treeMap = new TreeMap<>();
- 内部数据结构:
-
Hashtable:
- 内部数据结构:
Hashtable
也基于哈希表实现,类似于HashMap
。 - 无序性:
Hashtable
不保证键值对的顺序,元素的存储顺序是不确定的。 - 查找性能:与
HashMap
相似,具有快速的查找性能。 - 不允许空键和值:
Hashtable
不允许存储空键或空值。 - 线程安全:
Hashtable
是线程安全的,多个线程可以同时访问它。
Map<String, Integer> hashtable = new Hashtable<>();
- 内部数据结构:
选择哪种实现取决于需求:
- 如果需要一个无序的键值对集合,主要关注查找性能,
HashMap
是一个常用的选择。 - 如果需要一个有序的键值对集合,并关心插入顺序,
LinkedHashMap
可能是更合适的。 - 如果需要一个有序的键值对集合,同时需要按照特定顺序排序,
TreeMap
可以满足您的需求。 - 如果需要一个线程安全的键值对集合,
Hashtable
可能是一个选择,尽管在现代 Java 中,更多的人使用ConcurrentHashMap
来获得更好的性能。
无论您选择哪种实现,Map
接口提供了一种方便的方式来存储和检索键值对,可以根据具体的需求进行选择。
Comparator
Comparator
是 Java 中的一个接口,用于定义自定义比较规则,以便对对象进行排序。Comparator
接口通常与集合类(如 List
、TreeSet
、TreeMap
等)一起使用,以实现自定义排序。
Comparator
接口定义了一个用于比较两个对象的方法 compare
,该方法需要实现者提供比较规则。这个方法返回一个整数,表示两个对象的相对顺序。通常,返回值为负数表示第一个对象小于第二个对象,返回值为正数表示第一个对象大于第二个对象,返回值为零表示两个对象相等。
以下是 Comparator
接口的基本用法:
-
实现
Comparator
接口:创建一个实现了
Comparator
接口的类,并实现compare
方法以定义比较规则。比如,以下是一个用于对字符串按照长度进行排序的比较器:public class StringLengthComparator implements Comparator<String> { @Override public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); } }
-
使用
Comparator
进行排序:将实现了
Comparator
接口的比较器对象传递给集合类的排序方法。List<String> strings = new ArrayList<>(); strings.add("apple"); strings.add("banana"); strings.add("cherry"); Collections.sort(strings, new StringLengthComparator()); // 现在 strings 中的字符串按照长度排序
-
使用 lambda 表达式:
在 Java 8 及更高版本中,您可以使用 lambda 表达式来简化
Comparator
的创建。例如,上述的比较器可以用 lambda 表达式编写如下:Comparator<String> lengthComparator = (s1, s2) -> Integer.compare(s1.length(), s2.length()); Collections.sort(strings, lengthComparator);
-
逆序排序:
如果需要逆序排序,您可以使用
Collections.reverseOrder()
或Comparator.reverseOrder()
来创建逆序比较器。Comparator<String> reverseLengthComparator = Collections.reverseOrder(new StringLengthComparator()); // 或者 Comparator<String> reverseLengthComparator = Comparator.reverseOrder();
Comparator
接口使您能够灵活定义排序规则,因此您可以对集合中的对象进行多样化的排序。这对于自定义排序需求非常有用,如按不同属性、多条件排序等。
算法
Java Collections Framework提供了java.util.Collections
类,其中包含了各种用于操作集合的静态算法。这些算法可用于排序、查找、洗牌等各种集合操作。以下是一些常见的Collections
类中的算法:
- 排序(Sorting):
sort(List<T> list)
:对列表进行自然顺序排序。sort(List<T> list, Comparator<? super T> c)
:使用自定义比较器对列表进行排序。reverse(List<?> list)
:反转列表中的元素的顺序。
- 查找(Searching):
binarySearch(List<? extends Comparable<? super T>> list, T key)
:在已排序的列表中使用二分查找来查找元素。binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
:在已排序的列表中使用自定义比较器进行二分查找。
- 洗牌(Shuffling):
shuffle(List<?> list)
:随机重新排列列表中的元素。shuffle(List<?> list, Random rnd)
:使用指定的随机数生成器进行洗牌。
- 复制(Copying):
copy(List<? super T> dest, List<? extends T> src)
:将一个列表的内容复制到另一个列表。
- 填充(Filling):
fill(List<? super T> list, T obj)
:使用指定的元素填充列表。
- 最大值和最小值(Max and Min):
max(Collection<? extends T> coll)
:返回集合中的最大元素。max(Collection<? extends T> coll, Comparator<? super T> comp)
:使用自定义比较器返回最大元素。min(Collection<? extends T> coll)
:返回集合中的最小元素。min(Collection<? extends T> coll, Comparator<? super T> comp)
:使用自定义比较器返回最小元素。
- 不可修改的集合(Immutable Collections):
unmodifiableCollection(Collection<? extends T> c)
:将集合包装成不可修改的集合,防止修改操作。
- 同步集合(Synchronized Collections):
synchronizedCollection(Collection<T> c)
:将集合包装成同步集合,使其线程安全。
这些是一些常见的Collections
类中的算法。这些算法可用于对不同类型的集合进行操作,如List
、Set
和Map
。它们提供了一种便捷的方式来执行常见的集合操作,无需手动实现算法。