最近在整理Java整个集合框架相关的内容,有很多东西在日常项目开发中没有用到过,总结一篇博客给大家扫扫盲,此篇博客包含Java中所有集合容器的相关使用,如有遗漏还请指出;
在介绍集合框架之前,先来看一张图,俗话说不谋全局者不足以谋一域么,先整理了解一下集合框架都有哪些东西;
Java 集合框架可以分为两条大的支线:
ArrayList 是由数组实现的,支持随机存取,也就是可以通过下标直接存取元素;
从尾部插入和删除元素会比较快捷,从中间插入和删除元素会比较低效,因为涉及到数组元素的复制和移动;
如果内部数组的容量不足时会自动扩容,因此当元素非常庞大的时候,效率会比较低。
public static void main(String[] args) {
// 创建一个集合
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("www");
list.add("mmm");
list.add("jjj");
}
// 删除元素
public static void main(String[] args) {
// 创建一个集合
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("www");
list.add("mmm");
list.add("jjj");
// 删除元素
list.remove(1);
}
// 删除元素
public static void main(String[] args) {
// 创建一个集合
ArrayList<String> list = new ArrayList<>();
// 添加元素
list.add("www");
list.add("mmm");
list.add("jjj");
// 修改元素
list.set(1, "mjw");
}
// 遍历集合 for 循环
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
// 遍历集合 for each
for (String s : list) {
System.out.println(s);
}
LinkedList 是由双向链表实现的,不支持随机存取,只能从一端开始遍历,直到找到需要的元素后返回;
任意位置插入和删除元素都很方便,因为只需要改变前一个节点和后一个节点的引用即可,不像 ArrayList 那样需要复制和移动数组元素;
因为每个元素都存储了前一个和后一个节点的引用,所以相对来说,占用的内存空间会比 ArrayList 多一些。
public static void main(String[] args) {
// 创建一个集合
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("王");
list.add("沉");
list.add("qubg");
}
public static void main(String[] args) {
// 创建一个集合
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("你");
list.add("好");
list.add("呀");
// 删除元素
list.remove(1);
}
public static void main(String[] args) {
// 创建一个集合
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("你");
list.add("好");
list.add("呀");
// 修改元素
list.set(1, "二狗");
}
public static void main(String[] args) {
// 创建一个集合
LinkedList<String> list = new LinkedList<>();
// 添加元素
list.add("你");
list.add("好");
list.add("呀");
// 遍历集合 for 循环
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
// 遍历集合 for each
for (String s : list) {
System.out.println(s);
}
}
LinkedList也实现了 Deque 接口,可以作为队列来使用。等于说,LinkedList 同时实现了 Stack、Queue、PriorityQueue 的所有功能。
通过源码可以看到LinkedList同时实现了List和Deque接口,说明LinkedList是Java 集合框架中的双向队列。
List 的实现类还有一个 Vector,是一个元老级的类,比 ArrayList 出现得更早。ArrayList 和 Vector 非常相似,只不过 Vector 是线程安全的,像 get、set、add 这些方法都加了 synchronized 关键字,就导致执行执行效率会比较低,所以现在已经很少用了。
同时也可以看到JDK官方注释说明:如果不需要线程安全,建议使用ArrayList代替Vector。
Set 的特点是存取无序,不可以存放重复的元素,不可以用下标对元素进行操作,和 List 有很多不同。
废话不多话,直接看相关操作;
public static void main(String[] args) {
// 创建一个新的HashSet
HashSet<String> set = new HashSet<>();
// 添加元素
set.add("www");
set.add("王");
set.add("ddd");
// 输出HashSet的元素个数
System.out.println("HashSet size: " + set.size()); // output: 3
// 判断元素是否存在于HashSet中
boolean containsWanger = set.contains("王");
System.out.println("Does set contain '王'? " + containsWanger); // output: true
// 删除元素
boolean removeWanger = set.remove("王");
System.out.println("Removed '王'? " + removeWanger); // output: true
// 修改元素,需要先删除后添加
boolean removeChenmo = set.remove("ddd");
boolean addBuChenmo = set.add("vvvvvvv");
System.out.println("Modified set? " + (removeChenmo && addBuChenmo)); // output: true
// 输出修改后的HashSet
System.out.println("HashSet after modification: " + set);
}
LinkedHashSet 虽然继承自 HashSet,其实是由 LinkedHashMap 实现的。
看一下LinkedHashSet的构造方法:
其中在LinkedHashSet的无参构造方法中执行super(16,.75f,true),意思是执行父类的构造函数,继续执行可以看到:
实际new出来的是一个LinkedHashMap。
接下来看一段基于LinkedHashSet的相关操作:
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
// 添加元素
set.add("大家");
set.add("晚上");
set.add("好呀");
// 删除元素
set.remove("大家");
// 修改元素
set.remove("晚上");
set.add("中午");
// 查找元素
boolean hasChenQingYang = set.contains("好呀");
System.out.println("set包含好呀?" + hasChenQingYang);
}
LinkedHashSet是一种基于哈希表实现的Set接口,它继承自HashSet,并且使用链表维护了元素的插入顺序。因此,它既具有HashSet的快速查找、插入和删除操作的优点,又可以维护元素的插入顺序。
同样,TreeSet是基于TreeMap实现的,可以看TreeSet的构造方法:
TreeSet 是一种基于红黑树实现的有序集合,它实现了 SortedSet 接口,可以自动对集合中的元素进行排序。按照键的自然顺序或指定的比较器顺序进行排序。
public static void main(String[] args) {
// 创建一个 TreeSet 对象
TreeSet<String> set = new TreeSet<>();
// 添加元素
set.add("大家");
set.add("晚上");
set.add("好呀");
System.out.println(set); // 输出 [大家,晚上,好呀]
// 删除元素
set.remove("大家");
System.out.println(set); // 输出 [晚上,好呀]
// 修改元素:TreeSet 中的元素不支持直接修改,需要先删除再添加
set.remove("晚上");
set.add("中午");
System.out.println(set); // 输出 [中午,好呀]
// 查找元素
System.out.println(set.contains("晚上")); // 输出 true
System.out.println(set.contains("喵喵喵")); // 输出 false
}
队列,学习过基本数据结构的大家应该都知道,队列是遵循先进先出(FIFO)原则的。来看一个简易队列:
ArrayDeque 是一个基于数组实现的双端队列,为了满足可以同时在数组两端插入或删除元素的需求,数组必须是循环的,也就是说数组的任何一点都可以被看作是起点或者终点。
public static void main(String[] args) {
// 创建一个ArrayDeque
ArrayDeque<String> deque = new ArrayDeque<>();
//添加元素
deque.add("兄弟们");
deque.add("姐妹们");
deque.add("早上好");
deque.addFirst("1");
deque.addFirst("3");
deque.addLast("2");
System.out.println(deque.getLast());
System.out.println(deque.getFirst());
// 删除元素
deque.remove("兄弟们");
// 修改元素
deque.remove("姐妹们");
deque.add("大家");
// 查找元素
boolean hasChenQingYang = deque.contains("大家");
System.out.println("deque包含大家吗?" + hasChenQingYang);
}
LinkedList 和 ArrayDeque 都是 Java 集合框架中的双向队列(deque),它们都支持在队列的两端进行元素的插入和删除操作。不过,LinkedList 和 ArrayDeque 在实现上有一些不同:
因此,在选择使用 LinkedList 还是 ArrayDeque 时,需要根据具体的业务场景和需求来选择。如果需要在双向队列的两端进行频繁的插入和删除操作,并且需要随机访问元素,可以考虑使用 ArrayDeque;如果需要在队列中间进行频繁的插入和删除操作,可以考虑使用 LinkedList。
Map 保存的是键值对,键要求保持唯一性,值可以重复。
HashMap 实现了 Map 接口,可以根据键快速地查找对应的值——通过哈希函数将键映射到哈希表中的一个索引位置,从而实现快速访问。
HashMap 有一个初始容量和一个负载因子。初始容量是指哈希表的初始大小,负载因子是指哈希表在扩容之前可以存储的键值对数量与哈希表大小的比率。默认的初始容量是 16,负载因子是 0.75。
public static void main(String[] args) {
// 创建一个 HashMap 对象
HashMap<String, String> hashMap = new HashMap<>();
// 添加键值对
hashMap.put("wmj", "王梦杰");
hashMap.put("cyx", "陈奕迅");
hashMap.put("zj", "张杰");
// 获取指定键的值
String value1 = hashMap.get("zj");
System.out.println("zj对应的值为:" + value1);
// 修改键对应的值
hashMap.put("wmj", "wangmengjie");
String value2 = hashMap.get("沉默");
System.out.println("修改后wmj对应的值为:" + value2);
// 删除指定键的键值对
hashMap.remove("zj");
// 遍历 HashMap
for (String key : hashMap.keySet()) {
String value = hashMap.get(key);
System.out.println(key + " 对应的值为:" + value);
}
}
LinkedHashMap 是 HashMap 的子类,它使用链表来记录插入/访问元素的顺序。
LinkedHashMap 可以看作是 HashMap + LinkedList 的合体,它使用了哈希表来存储数据,又用了双向链表来维持顺序。
来看一下LinkedHashMap和HashMap对比程序:
首先是LinkedHashMap:创建了一个LinkedHashMap,插入几个键值对,然后遍历打印结果;
public static void main(String[] args) {
// 创建一个 LinkedHashMap,插入键值对
LinkedHashMap<String, String> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("wmj", "王梦杰");
linkedHashMap.put("cyx", "陈奕迅");
linkedHashMap.put("zj", "张杰");
// 遍历 LinkedHashMap
for (String key : linkedHashMap.keySet()) {
String value = linkedHashMap.get(key);
System.out.println(key + " 对应的值为:" + value);
}
}
从打印结果我们得知,输出打印的次序和插入的顺序是一致的,说明LinkedHashMap是一个有序的键值对集合;
接下来可以看一下HashMap的打印结果产生一下对比:
public static void main(String[] args) {
// 创建一个 HashMap 对象
HashMap<String, String> hashMap = new HashMap<>();
// 添加键值对
hashMap.put("wmj", "王梦杰");
hashMap.put("cyx", "陈奕迅");
hashMap.put("zj", "张杰");
// 遍历 HashMap
for (String key : hashMap.keySet()) {
String value = hashMap.get(key);
System.out.println(key + " 对应的值为:" + value);
}
}
TreeMap实现了 SortedMap 接口,可以自动将键按照自然顺序或指定的比较器顺序排序,并保证其元素的顺序。内部使用红黑树来实现键的排序和查找。
public static void main(String[] args) {
// 创建一个 TreeMap 对象
Map<String, String> treeMap = new TreeMap<>();
// 向 TreeMap 中添加键值对
treeMap.put("wmj", "王梦杰");
treeMap.put("cyx", "陈奕迅");
treeMap.put("zj", "张杰");
// 查找键值对
String name = "wmj";
if (treeMap.containsKey(name)) {
System.out.println("找到了 " + name + ": " + treeMap.get(name));
} else {
System.out.println("没有找到 " + name);
}
// 修改键值对
name = "wmj";
if (treeMap.containsKey(name)) {
System.out.println("修改前的" + name + ": " + treeMap.get(name));
treeMap.put(name, "wangmengjie");
System.out.println("修改后的 " + name + ": " + treeMap.get(name));
} else {
System.out.println("没有找到 " + name);
}
// 删除键值对
name = "zj";
if (treeMap.containsKey(name)) {
System.out.println("删除前的 " + name + ": " + treeMap.get(name));
treeMap.remove(name);
System.out.println("删除后的 " + name + ": " + treeMap.get(name));
} else {
System.out.println("没有找到 " + name);
}
// 遍历 TreeMap
for (Map.Entry<String, String> entry : treeMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
此篇文章总结了常用集合的一些基本操作,当然,我们不能只会使用它,更加去研究它是如何实现的,这样才能去学习它,成为它,超越它。
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏哦。