J03 Java基础 03:集合框架
集合框架
Java 集合框架(Java Collections Framework, JCF) 是 Java
提供的一套标准化 API,用于处理集合(Collection)类的数据结构。
Java 集合框架包含以下几个核心部分:
接口(Interfaces):定义了集合的基本行为规范,即上图黄色部分。
实现类(Implementations):为接口提供具体实现,即上图蓝色和紫色部分。
工具类(Utility
Classes):提供对集合的操作支持,如排序、查找等。
在使用时,接口是不能直接实现的,需要使用具体的类实现。
collection接口
collection接口是所有集合类的跟接口,可以使用集合类的6种方式实现。
例如学生成绩储存的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class StudentScoreManager { public static void main (String[] args) { Collection<Double> scores = new ArrayList <>(); **scores.add(85.5 );** scores.add(90.0 ); scores.add(78.5 ); scores.add(88.0 ); System.out.println("所有学生成绩:" + scores); double targetScore = 90.0 ; if (**scores.contains(targetScore)**) { System.out.println("成绩 " + targetScore + " 存在于集合中。" ); } else { System.out.println("成绩 " + targetScore + " 不存在于集合中。" ); } **scores.remove(78.5 );** System.out.println("移除78.5后的成绩:" + scores); double total = 0.0 ; for (**double score : scores**) { total += score; } double average = total / **scores.size()**; System.out.println("总成绩:" + total + ",平均成绩:" + average); if (**scores.isEmpty()**) { System.out.println("集合为空。" ); } else { System.out.println("集合中有 " + scores.size() + " 个成绩。" ); } **scores.clear()**; System.out.println("清空后,集合:" + scores); } } --- 所有学生成绩:[85.5 , 90.0 , 78.5 , 88.0 ] 成绩 90.0 存在于集合中。 移除78.5 后的成绩:[85.5 , 90.0 , 88.0 ] 总成绩:263.5 ,平均成绩:87.83333333333333 集合中有 3 个成绩。 清空后,集合:[]
上面提供的例子中包括了Collection
最为常用的几个api:
add
,contrains
,remove
,size
,isEmpty
,clear
值得注意的是,在collection中, add
,contrains
,remove
都仅支持单一的参数Object o
以及用于比较两个集合元素是否相等的
equals
Collection
同时还有迭代器Iterator
接口,用于遍历集合中的元素。
Iterator
接口提供以下核心方法:
hasNext()
检查是否还有元素可供迭代。
返回值:true
表示有下一个元素,false
表示没有。
next()
返回迭代的下一个元素。
如果没有元素,则抛出 NoSuchElementException
。
remove()
删除最近一次通过 next()
方法返回的元素。
注意:此方法是可选操作,并且只能在调用 next()
之后调用一次。
1 2 3 4 5 6 7 8 9 Iterator<Double> iterator = scores.iterator(); while (iterator.hasNext()) { double score = iterator.next(); if (score < 80 ) { System.out.println("移除低于 80 的成绩:" + score); iterator.remove(); } }
List接口
List是继承自 Collection
接口的子接口,表示一个有序的元素集合,List
是专门为需要按照插入顺序存储和访问元素的场景设计的。按照插入顺序进行存储,允许元素重复,可以通过索引快速访问元素。List
的大小是动态的,不需要像数组那样提前定义长度。
对于list接口的api,可以从collection的不便之处的角度出发——不支持索引。
因此List接口的主要重写了collection用来增减集合元素的add
,remove
几个函数,同时增加了set
,
get
,indexOf
这些用于控制元素组成的方法。在list版本中,这些方法接受的参数分别为:
add(int index,E e)
set(int index,E e)
get(int index)
remove(int index)
indexOf(E e)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class ListOperationsDemo { public static void main(String[] args) { // **初始化List ** List <String> names = new ArrayList<>(); names.add("tomori" ); names.add("soyo" ); names.add("sakiko" ); names.add("mutsumi" ); names.add("taki" ); System.out.println("初始列表: " + names); // **依次移除元素** names.remove(2 ); // 移除 "sakiko" System.out.println("移除 'sakiko': " + names); names.remove(2 ); // 移除 "mutsumi" System.out.println("移除 'mutsumi': " + names); names.remove(2 ); // 移除 "taki" System.out.println("移除 'taki': " + names); names.remove(1 ); // 移除 "soyo" System.out.println("移除 'soyo': " + names); // **添加新元素** names.add("anon" ); // 加入 "anon" System.out.println("加入 'anon': " + names); names.add("soyo" ); // 再次加入 "soyo" System.out.println("加入 'soyo': " + names); names.add("taki" ); // 再次加入 "taki" System.out.println("加入 'taki': " + names); // **设置特定位置的元素** names.set (names.indexOf("taki" ), "rikki" ); // 将 "taki" 设置为 "rikki" System.out.println("将 'taki' 设置为 'rikki': " + names); names.add("laana" ); // 加入 "laana" System.out.println("加入 'laana': " + names); names.set (names.indexOf("tomori" ), "tomorin" ); // 将 "tomori" 设置为 "tomorin" System.out.println("将 'tomori' 设置为 'tomorin': " + names); names.set (names.indexOf("soyo" ), "soyorin" ); // 将 "soyo" 设置为 "soyorin" System.out.println("将 'soyo' 设置为 'soyorin': " + names); // **最终列表** System.out.println("最终列表: " + names); } }
LinkedList
ArrayList
Vector
底层实现
双向链表
动态数组
动态数组
线程安全
否
否
是
随机访问效率
低(O(n)
)
高(O(1)
)
高(O(1)
)
插入删除效率
高(O(1)
)
低(O(n)
)
低(O(n)
)
扩容方式
不适用
容量增加 1.5 倍
容量翻倍
使用场景
频繁插入和删除
随机访问频繁
线程安全的场景
Set 接口
Set
接口是一个不允许重复元素的集合,不能包含两个相等的元素。元素的唯一性是基于equals()
方法的定义。
根据不同的实现方式,Set可能会保留插入顺序,完全无序,或者按照某种方式排序。因此没有索引,不能通过索引访问。其操作与Collection很像:
boolean add(E e)
:向集合中添加元素,如果元素已存在,返回false
。
boolean remove(Object o)
:移除指定的元素,成功返回true
。
boolean contains(Object o)
:检查集合是否包含某个元素。
void clear()
:清空集合中的所有元素。
int size()
:返回集合中元素的数量。
boolean isEmpty()
:检查集合是否为空。
如上所述,Set
接口的主要实现方式之间有一定的区别。
HashSet
基于哈希表实现,使用HashMap
作为底层数据结构。
不保证元素的顺序。
插入、删除、查找操作的时间复杂度为O(1)
(在哈希函数表现良好的情况下)。
LinkedHashSet
基于哈希表和链表实现,底层使用LinkedHashMap
。LinkedHashMap
是 HashMap
的一个子类,它在存储键值对的同时,通过维护一个双向链表来记录元素的插入顺序。
保留元素的插入顺序。
时间复杂度略高于HashSet
,但仍为O(1)
。
TreeSet
基于红黑树实现,底层使用NavigableMap
(通常是TreeMap
)。
保证元素的自然顺序(或通过提供的Comparator
定义的顺序)。
插入、删除、查找操作的时间复杂度为O(log n)
。
Map接口
Map
接口是集合框架中用于存储键值对(key-value)的数据结构,很像python中的字典。
与Set
相似,HashMap
不保证元素顺序,而
LinkedHashMap
保留插入顺序,TreeMap
保证键的自然顺序或自定义顺序。
由于Map
和Collection
并不存在继承关系,他们的方法相差较大。大致上包括以下方法。
put(K key, V value)
:如果键已存在,返回旧值;否则返回
null
。
get(Object key)
:如果键不存在,返回
null
。
remove(Object key)
:如果键存在,返回旧值;否则返回
null
。
containsKey(Object key)
containsValue(Object value)
size()
isEmpty()
clear()
keySet()
:返回所有键的集合(Set
)。
values()
:返回所有值的集合(Collection
)。
entrySet()
:返回所有键值对的集合(Set<Map.Entry<K, V>>
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 import java.util.HashMap;import java.util.Map;import java.util.Set ;import java.util.Collection;public class MyGOBandDemo { public static void main(String[] args) { // 创建一个 HashMap 实例 Map<String, String> mygoBand = new HashMap<>(); // 使用 put 方法添加键值对 mygoBand.put("主唱" , "高松灯" ); mygoBand.put("吉他手" , "千早爱音" ); mygoBand.put("主音吉他手" , "要乐奈" ); mygoBand.put("贝斯手" , "长崎爽世" ); mygoBand.put("鼓手" , "椎名立希" ); // 使用 get 方法获取键对应的值 String vocalist = mygoBand.get("主唱" ); System.out.println("主唱是: " + vocalist); // 使用 containsKey 方法检查 Map 中是否包含某个键 boolean hasDrummer = mygoBand.containsKey("鼓手" ); System.out.println("乐队中是否有鼓手? " + hasDrummer); // 使用 containsValue 方法检查 Map 中是否包含某个值 boolean hasMember = mygoBand.containsValue("千早爱音" ); System.out.println("乐队中是否有名为千早爱音的成员? " + hasMember); // 使用 size 方法获取 Map 中的键值对数量 int bandSize = mygoBand.size(); System.out.println("乐队成员数量: " + bandSize); // 使用 isEmpty 方法检查 Map 是否为空 boolean isBandEmpty = mygoBand.isEmpty(); System.out.println("乐队成员列表是否为空? " + isBandEmpty); // 使用 keySet 方法获取所有键的集合 Set <String> roles = mygoBand.keySet(); System.out.println("乐队中的角色: " + roles); // 使用 values 方法获取所有值的集合 Collection<String> members = mygoBand.values(); System.out.println("乐队成员的名字: " + members); // 使用 entrySet 方法获取所有键值对的集合 Set <Map.Entry<String, String>> entries = mygoBand.entrySet(); System.out.println("乐队成员列表:" ); for (Map.Entry<String, String> entry : entries) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // 使用 remove 方法删除指定键的键值对 String removedMember = mygoBand.remove("贝斯手" ); System.out.println("被移除的成员: " + removedMember); // 使用 clear 方法清空 Map 中的所有键值对 mygoBand.clear(); System.out.println("清空后的乐队成员列表: " + mygoBand); } }
Iterator迭代器和ListIterator迭代器
在collection部分简单提及了迭代器的使用。对于这些迭代器来说,它们的主要方法包括hasNext()
,next()
,remove()
。因此,迭代器提供了一个遍历和修改集合中元素的方法。
但需要注意的是,在使用迭代器的同时不能使用如add
这些方法修改集合。
如果希望同时使用迭代器和修改元素,或者希望使用双向遍历等功能,可以使用ListIterator
。这是专门为List设计的迭代器。
功能
Iterator
ListIterator
单向遍历
✅
✅
双向遍历
❌
✅ (hasPrevious()
,previous()
)
移除元素
✅(remove()
)
✅(remove()
)
添加元素
❌
✅(add(E e)
)
修改元素
❌
✅(set(E e)
)
索引支持
❌
✅(nextIndex()
,previousIndex()
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 package collections; import java.util.ArrayList;import java.util.Iterator;import java.util.List ;import java.util.ListIterator;public class ListIteratorDemo { public static void main(String[] args) { // **初始化列表** List <String> list = new ArrayList<>(); list .add("A" ); list .add("B" ); list .add("C" ); list .add("D" ); System.out.println("初始列表: " + list ); // **演示Iterator不能同时迭代和add操作** demonstrateIteratorLimitations(list ); // **演示ListIterator解决方案** demonstrateListIteratorSolution(list ); } private static void demonstrateIteratorLimitations(List <String> list ) { System.out.println("\n使用Iterator尝试迭代和添加元素:" ); try { Iterator<String> iterator = list .iterator(); while (iterator.hasNext()) { String element = iterator.next (); System.out.println("当前元素: " + element); if (element.equals("B" )) { list .add("E" ); // 尝试在迭代过程中添加元素 } } } catch (Exception e) { System.out.println("发生异常: " + e.getClass().getSimpleName() + " - " + e.getMessage()); } System.out.println("Iterator操作后的列表: " + list ); } private static void demonstrateListIteratorSolution(List <String> list ) { System.out.println("\n使用ListIterator同时迭代和添加元素:" ); ListIterator<String> listIterator = list .listIterator(); while (listIterator.hasNext()) { String element = listIterator.next (); System.out.println("当前元素: " + element); if (element.equals("B" )) { listIterator.add("E" ); // 使用ListIterator添加元素 System.out.println("添加元素 'E' 到列表" ); } } System.out.println("ListIterator操作后的列表: " + list ); } } --- 初始列表: [A, B, C, D] 使用Iterator尝试迭代和添加元素: 当前元素: A 当前元素: B 发生异常: ConcurrentModificationException - null Iterator操作后的列表: [A, B, C, D, E] 使用ListIterator同时迭代和添加元素: 当前元素: A 当前元素: B 添加元素 'E' 到列表 当前元素: C 当前元素: D 当前元素: E ListIterator操作后的列表: [A, B, E, C, D, E]
Iterator
适用于简单的只读遍历,而ListIterator
适用于需要在遍历过程中动态修改列表内容的场景。
参考
https://blog.csdn.net/grd_java/article/details/122403602
https://zh.wikipedia.org/zh-cn/Java%E9%9B%86%E5%90%88%E6%A1%86%E6%9E%B6
https://www.runoob.com/java/java-collections.html