好的,各位程序猿、攻城狮、代码界的艺术家们,今天咱们来聊聊Java集合框架这个“老朋友”。别看它“老”,用起来可是相当“骚”气!咱们要像老司机一样,把Collection和Map这两大接口体系摸得门儿清,这样才能在代码的世界里驰骋自如,写出高效优雅的程序。
一、Java集合框架:一个江湖,两种势力
你可以把Java集合框架想象成一个武林,里面高手如云,秘籍无数。而Collection和Map,就是这个武林中两大势力。
-
Collection势力:单身贵族的聚集地
Collection接口代表的是一组对象,每个对象都是独立的个体。你可以把它看作一个单身俱乐部,里面的每个成员都是自由的灵魂,彼此之间没有必然的联系。Collection下面又分了三个分支:
-
List:有序可重复的队伍
List接口就像一支训练有素的军队,里面的元素按照特定的顺序排列,而且允许有重复的士兵。ArrayList和LinkedList就是这支军队里最著名的两个兵种。
-
ArrayList:速度型选手
ArrayList底层是基于数组实现的,所以它在随机访问元素时速度飞快,就像博尔特一样。但是,在插入和删除元素时,需要移动大量的元素,就像搬家一样费劲。
List<String> arrayList = new ArrayList<>(); arrayList.add("Java"); arrayList.add("Python"); arrayList.add("C++"); System.out.println(arrayList.get(1)); // 输出:Python arrayList.add(1, "JavaScript"); // 在索引1处插入元素 System.out.println(arrayList); // 输出:[Java, JavaScript, Python, C++]
-
LinkedList:灵活型选手
LinkedList底层是基于链表实现的,所以它在插入和删除元素时非常灵活,就像泥鳅一样滑溜。但是,在随机访问元素时,需要从头开始遍历,就像大海捞针一样费劲。
List<String> linkedList = new LinkedList<>(); linkedList.add("Java"); linkedList.add("Python"); linkedList.add("C++"); linkedList.addFirst("JavaScript"); // 在列表头部添加元素 linkedList.addLast("Go"); // 在列表尾部添加元素 System.out.println(linkedList.getFirst()); // 输出:JavaScript System.out.println(linkedList.getLast()); // 输出:Go
List的特点:
特点 ArrayList LinkedList 底层实现 数组 链表 访问速度 随机访问快 随机访问慢 增删速度 增删慢 增删快 内存占用 占用连续空间 占用分散空间 适用场景 频繁访问元素 频繁增删元素 -
-
Set:独一无二的集合
Set接口就像一个高冷的俱乐部,里面的每个成员都是独一无二的,不允许有重复的。HashSet和TreeSet就是这个俱乐部里最受欢迎的两个派系。
-
HashSet:无序的快速查找
HashSet底层是基于哈希表实现的,所以它在查找元素时速度非常快,就像警察抓小偷一样高效。但是,HashSet里面的元素是无序的,就像一群自由散漫的艺术家。
Set<String> hashSet = new HashSet<>(); hashSet.add("Java"); hashSet.add("Python"); hashSet.add("C++"); hashSet.add("Java"); // 重复元素,不会被添加 System.out.println(hashSet.contains("Python")); // 输出:true System.out.println(hashSet); // 输出:[Java, Python, C++] (顺序不确定)
-
TreeSet:有序的自动排序
TreeSet底层是基于红黑树实现的,所以它里面的元素是有序的,就像参加选美比赛的佳丽一样,按照一定的规则排列。但是,TreeSet在插入和删除元素时,需要维护树的结构,所以速度相对较慢。
Set<String> treeSet = new TreeSet<>(); treeSet.add("Java"); treeSet.add("Python"); treeSet.add("C++"); System.out.println(treeSet); // 输出:[C++, Java, Python] (按照字母顺序排序) Set<Integer> treeSet2 = new TreeSet<>((a, b) -> b - a); // 自定义排序规则,降序 treeSet2.add(10); treeSet2.add(5); treeSet2.add(15); System.out.println(treeSet2); // 输出:[15, 10, 5]
Set的特点:
特点 HashSet TreeSet 底层实现 哈希表 红黑树 元素顺序 无序 有序 查找速度 查找快 查找相对慢 元素是否重复 不允许重复 不允许重复 适用场景 需要快速查找元素 需要有序存储元素 -
-
Queue:先进先出的队列
Queue接口就像一个排队等候的队伍,里面的元素按照先进先出的原则排列。PriorityQueue和ArrayDeque就是这个队伍里比较特殊的两个成员。
-
PriorityQueue:优先级队列
PriorityQueue可以根据元素的优先级进行排序,优先级高的元素会优先出队。就像医院里的急诊病人,会优先得到治疗。
Queue<Integer> priorityQueue = new PriorityQueue<>(); priorityQueue.add(10); priorityQueue.add(5); priorityQueue.add(15); System.out.println(priorityQueue.poll()); // 输出:5 (默认按照从小到大排序) Queue<Integer> priorityQueue2 = new PriorityQueue<>((a, b) -> b - a); // 自定义优先级,降序 priorityQueue2.add(10); priorityQueue2.add(5); priorityQueue2.add(15); System.out.println(priorityQueue2.poll()); // 输出:15
-
ArrayDeque:双端队列
ArrayDeque是一个双端队列,可以在队列的头部和尾部进行插入和删除操作。就像一个可以双向行驶的隧道。
Deque<String> arrayDeque = new ArrayDeque<>(); arrayDeque.addFirst("Java"); arrayDeque.addLast("Python"); arrayDeque.offerFirst("JavaScript"); arrayDeque.offerLast("Go"); System.out.println(arrayDeque.pollFirst()); // 输出:JavaScript System.out.println(arrayDeque.pollLast()); // 输出:Go
Queue的特点:
特点 PriorityQueue ArrayDeque 元素顺序 优先级排序 先进先出 插入位置 根据优先级 头部和尾部 删除位置 优先级最高的元素 头部和尾部 适用场景 需要优先级排序 需要双端操作 -
-
-
Map势力:情侣的聚集地
Map接口代表的是一组键值对,每个键都对应一个值。你可以把它看作一个婚姻介绍所,里面的每对情侣都是紧密联系的,通过键可以找到对应的值。HashMap和TreeMap就是这个介绍所里最受欢迎的两个服务项目。
-
HashMap:无序的快速查找
HashMap底层是基于哈希表实现的,所以它在查找元素时速度非常快,就像搜索引擎一样高效。但是,HashMap里面的元素是无序的,就像一群自由恋爱的年轻人。
Map<String, Integer> hashMap = new HashMap<>(); hashMap.put("Java", 1); hashMap.put("Python", 2); hashMap.put("C++", 3); System.out.println(hashMap.get("Python")); // 输出:2 System.out.println(hashMap.containsKey("Java")); // 输出:true System.out.println(hashMap.containsValue(2)); // 输出:true // 遍历HashMap for (Map.Entry<String, Integer> entry : hashMap.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // 使用keySet()遍历 for(String key : hashMap.keySet()){ System.out.println(key + ": " + hashMap.get(key)); } // 使用values()遍历 for(Integer value : hashMap.values()){ System.out.println(value); }
-
TreeMap:有序的自动排序
TreeMap底层是基于红黑树实现的,所以它里面的元素是有序的,就像参加相亲大会的情侣一样,按照一定的规则排列。但是,TreeMap在插入和删除元素时,需要维护树的结构,所以速度相对较慢。
Map<String, Integer> treeMap = new TreeMap<>(); treeMap.put("Java", 1); treeMap.put("Python", 2); treeMap.put("C++", 3); System.out.println(treeMap); // 输出:{C++=3, Java=1, Python=2} (按照键的字母顺序排序) Map<String, Integer> treeMap2 = new TreeMap<>((a, b) -> b.compareTo(a)); // 自定义排序规则,键的降序 treeMap2.put("Java", 1); treeMap2.put("Python", 2); treeMap2.put("C++", 3); System.out.println(treeMap2); // 输出:{Python=2, Java=1, C++=3}
Map的特点:
特点 HashMap TreeMap 底层实现 哈希表 红黑树 元素顺序 无序 有序 查找速度 查找快 查找相对慢 键是否重复 不允许重复 不允许重复 适用场景 需要快速查找元素 需要有序存储元素 -
二、Collection与Map的区别:单身和情侣的区别
Collection和Map最大的区别在于,Collection存储的是单个对象,而Map存储的是键值对。你可以把Collection想象成一个单身俱乐部,里面的每个成员都是独立的个体,而Map想象成一个婚姻介绍所,里面的每对情侣都是紧密联系的。
三、选择合适的集合:因地制宜,量体裁衣
在实际开发中,选择合适的集合非常重要。就像选择合适的武器一样,用对了可以事半功倍,用错了可能会适得其反。
-
如果需要存储一组有序且可重复的元素,可以选择List。
- 如果需要频繁访问元素,可以选择ArrayList。
- 如果需要频繁插入和删除元素,可以选择LinkedList。
-
如果需要存储一组唯一且无序的元素,可以选择Set。
- 如果需要快速查找元素,可以选择HashSet。
- 如果需要有序存储元素,可以选择TreeSet。
-
如果需要存储一组键值对,可以选择Map。
- 如果需要快速查找元素,可以选择HashMap。
- 如果需要有序存储元素,可以选择TreeMap。
-
其他特殊场景:
- 需要优先级队列时,选择PriorityQueue。
- 需要双端队列时,选择ArrayDeque。
- 需要线程安全的集合时,可以选择ConcurrentHashMap、CopyOnWriteArrayList等。
四、实战演练:代码才是硬道理
光说不练假把式,咱们来几个实战演练,看看如何运用这些集合。
-
统计字符串中每个字符出现的次数
String str = "Hello World!"; Map<Character, Integer> charCountMap = new HashMap<>(); for (char c : str.toCharArray()) { charCountMap.put(c, charCountMap.getOrDefault(c, 0) + 1); } System.out.println(charCountMap); // 输出:{ =1, d=1, r=1, H=1, e=1, l=3, W=1, o=2, !=1}
-
去除List中的重复元素
List<String> list = new ArrayList<>(); list.add("Java"); list.add("Python"); list.add("Java"); list.add("C++"); Set<String> uniqueSet = new HashSet<>(list); List<String> uniqueList = new ArrayList<>(uniqueSet); System.out.println(uniqueList); // 输出:[Java, Python, C++] (顺序不确定)
-
对学生成绩进行排序(按照成绩从高到低)
class Student { String name; int score; public Student(String name, int score) { this.name = name; this.score = score; } public String getName() { return name; } public int getScore() { return score; } @Override public String toString() { return "Student{" + "name='" + name + ''' + ", score=" + score + '}'; } } public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("Alice", 80)); students.add(new Student("Bob", 90)); students.add(new Student("Charlie", 70)); // 使用Collections.sort()进行排序 Collections.sort(students, (a, b) -> b.getScore() - a.getScore()); System.out.println(students); // 输出:[Student{name='Bob', score=90}, Student{name='Alice', score=80}, Student{name='Charlie', score=70}] }
五、总结:掌握集合,走向人生巅峰
Java集合框架是Java编程中非常重要的一个组成部分。掌握了Collection和Map这两大接口体系,就相当于掌握了Java编程的半壁江山。希望这篇文章能帮助大家更好地理解Java集合框架,并在实际开发中灵活运用,写出更加高效优雅的代码。
记住,代码的世界是无限的,学习永无止境。加油,各位未来的编程大师们!