当前位置: 首页 > news >正文

基础14-Java集合框架:掌握List、Set和Map的使用

在Java编程中,数据的组织与管理是构建高效、可维护应用程序的核心。Java集合框架(Java Collections Framework)为此提供了强大而灵活的工具集。它是一组接口和类的集合,用于存储、操作和处理对象组。理解并熟练掌握ListSetMap这三大核心接口及其常用实现类,是每个Java开发者必备的技能。本文将深入探讨这些集合的特性、用法、性能差异以及最佳实践,通过丰富的代码示例帮助你全面掌握Java集合框架。


一、Java集合框架概览

Java集合框架位于java.util包中,其设计基于一组核心接口,这些接口定义了集合的行为。主要的接口包括:

  • Collection:所有单值集合(存储单个元素的集合)的根接口,ListSet都继承自它。
  • List:有序集合,允许重复元素,可以通过索引访问。
  • Set:不允许重复元素的集合,通常无序(但有例外)。
  • Queue:用于模拟队列数据结构,通常遵循先进先出(FIFO)原则。
  • Deque:双端队列,支持在两端插入和删除元素。
  • Map:存储键值对(key-value pairs)的集合,键不允许重复。

集合框架的设计遵循“接口优先”的原则,允许我们编写与具体实现无关的代码,从而提高代码的灵活性和可重用性。

1.1 集合框架的层次结构

                              Collection/ \/   \/     \List     Set|        ||        |ArrayList  HashSetLinkedList   LinkedHashSetVector       TreeSetStack
                              Map||HashMapLinkedHashMapTreeMapHashtable

1.2 使用集合框架的优势

  1. 高性能:经过优化的实现提供了高效的增删改查操作。
  2. 可重用性:提供了一套标准的数据结构和算法。
  3. 类型安全(结合泛型):在编译期检查类型,避免运行时错误。
  4. 易于使用:统一的接口和丰富的API。
  5. 可扩展性:可以轻松地创建自定义集合实现。

二、List接口:有序的序列

List接口代表一个有序的集合,也称为序列。它允许存储重复元素,并且可以通过元素的整数索引(位置)来访问元素。

2.1 List的核心特性

  • 有序性:元素的插入顺序被保留。
  • 可重复:同一个元素可以出现多次。
  • 索引访问:支持通过get(int index)set(int index, E element)等方法进行随机访问。
  • 常用方法
    • add(E e) / add(int index, E element):添加元素。
    • remove(int index) / remove(Object o):移除元素。
    • get(int index):获取指定位置的元素。
    • set(int index, E element):替换指定位置的元素。
    • indexOf(Object o) / lastIndexOf(Object o):查找元素的索引。
    • size():返回元素数量。
    • isEmpty():检查是否为空。
    • contains(Object o):检查是否包含某个元素。

2.2 ArrayList:动态数组

ArrayListList接口最常用的实现,基于动态数组。它提供了快速的随机访问,但在列表中间插入或删除元素时性能较差,因为需要移动后续元素。

2.2.1 基本使用
import java.util.ArrayList;
import java.util.List;public class ArrayListExample {public static void main(String[] args) {// 创建一个ArrayListList<String> fruits = new ArrayList<>();// 添加元素fruits.add("Apple");fruits.add("Banana");fruits.add("Orange");fruits.add("Apple"); // 允许重复System.out.println("Fruits: " + fruits); // [Apple, Banana, Orange, Apple]// 通过索引访问String firstFruit = fruits.get(0);System.out.println("First fruit: " + firstFruit); // Apple// 修改元素fruits.set(1, "Grape");System.out.println("After set: " + fruits); // [Apple, Grape, Orange, Apple]// 插入元素fruits.add(1, "Mango");System.out.println("After insert: " + fruits); // [Apple, Mango, Grape, Orange, Apple]// 删除元素fruits.remove("Apple"); // 删除第一个匹配的元素System.out.println("After remove: " + fruits); // [Mango, Grape, Orange, Apple]fruits.remove(0); // 删除索引为0的元素System.out.println("After remove by index: " + fruits); // [Grape, Orange, Apple]// 查找元素int indexOfOrange = fruits.indexOf("Orange");System.out.println("Index of Orange: " + indexOfOrange); // 1// 遍历System.out.println("Fruits:");for (String fruit : fruits) {System.out.println("- " + fruit);}}
}
2.2.2 性能特点
  • 随机访问O(1) - 非常快。
  • 在末尾添加/删除O(1) - 平均情况下很快(扩容时为O(n))。
  • 在中间或开头插入/删除O(n) - 需要移动元素。
  • 查找O(n) - 需要遍历。

初始容量与扩容ArrayList有一个初始容量(默认为10),当元素数量超过容量时,会自动扩容(通常增加50%),并复制所有元素到新数组。如果知道大致元素数量,建议在创建时指定初始容量以避免频繁扩容。

// 指定初始容量
List<Integer> numbers = new ArrayList<>(100);

2.3 LinkedList:双向链表

LinkedList基于双向链表实现。它在列表的任意位置插入和删除元素都非常高效,但随机访问性能较差。

2.3.1 基本使用
import java.util.LinkedList;
import java.util.List;public class LinkedListExample {public static void main(String[] args) {List<String> tasks = new LinkedList<>();tasks.add("Task 1");tasks.add("Task 2");tasks.add("Task 3");System.out.println("Tasks: " + tasks);// LinkedList还实现了Deque接口,支持双端操作((LinkedList<String>) tasks).addFirst("Priority Task");((LinkedList<String>) tasks).addLast("Final Task");System.out.println("After addFirst/addLast: " + tasks);String firstTask = ((LinkedList<String>) tasks).removeFirst();String lastTask = ((LinkedList<String>) tasks).removeLast();System.out.println("Removed first: " + firstTask); // Priority TaskSystem.out.println("Removed last: " + lastTask);   // Final TaskSystem.out.println("After removeFirst/removeLast: " + tasks);}
}
2.3.2 性能特点
  • 在任意位置插入/删除O(1) - 只需修改指针。
  • 随机访问O(n) - 需要从头或尾遍历。
  • 查找O(n)

何时使用LinkedList

  • 需要频繁在列表中间插入或删除元素。
  • 需要实现栈(push/pop)或队列(offer/poll)数据结构。

2.4 Vector和Stack

VectorArrayList的线程安全版本,其方法大多使用synchronized修饰。由于性能开销,现代开发中通常使用ArrayList配合Collections.synchronizedList()CopyOnWriteArrayList来实现线程安全。

Stack继承自Vector,实现了后进先出(LIFO)的栈。但由于其继承自Vector,且有更好的替代品(如ArrayDeque),不推荐使用。

// 不推荐
Stack<String> stack = new Stack<>();
stack.push("A");
stack.push("B");
String top = stack.pop();// 推荐使用ArrayDeque作为栈
import java.util.ArrayDeque;
ArrayDeque<String> dequeStack = new ArrayDeque<>();
dequeStack.push("A");
dequeStack.push("B");
String topDeque = dequeStack.pop();

2.5 List的遍历方式

import java.util.*;public class ListTraversal {public static void main(String[] args) {List<String> list = Arrays.asList("A", "B", "C", "D");// 1. 增强for循环(推荐)System.out.println("Enhanced for loop:");for (String item : list) {System.out.println(item);}// 2. 迭代器(Iterator)System.out.println("Iterator:");Iterator<String> iterator = list.iterator();while (iterator.hasNext()) {String item = iterator.next();System.out.println(item);// iterator.remove(); // 如果需要在遍历中删除元素}// 3. 列表迭代器(ListIterator)- 支持双向遍历和修改System.out.println("ListIterator (backward):");ListIterator<String> listIterator = list.listIterator(list.size());while (listIterator.hasPrevious()) {String item = listIterator.previous();System.out.println(item);// listIterator.set("Modified"); // 修改当前元素// listIterator.add("New"); // 在当前位置插入}// 4. 传统for循环(索引)System.out.println("Traditional for loop:");for (int i = 0; i < list.size(); i++) {String item = list.get(i);System.out.println(item);}// 5. Java 8 Stream APISystem.out.println("Stream API:");list.stream().forEach(System.out::println);}
}

注意:在使用迭代器遍历集合时,如果需要删除元素,必须使用iterator.remove()方法,直接调用list.remove()会导致ConcurrentModificationException


三、Set接口:无重复元素的集合

Set接口表示一个不包含重复元素的集合。它继承自Collection,并添加了对重复元素的限制。Set通常不保证元素的顺序,但有例外。

3.1 Set的核心特性

  • 无重复:不能包含两个相等的元素(根据equals()方法判断)。
  • 无序性:大多数实现不保证元素的顺序(但LinkedHashSetTreeSet例外)。
  • 常用方法:与Collection接口基本相同,如add(), remove(), contains(), size()等。add()方法在元素已存在时返回false,否则返回true

3.2 HashSet:基于哈希表

HashSetSet接口最常用的实现,基于HashMap。它提供最快的查找、添加和删除操作,但不保证元素的顺序。

3.2.1 基本使用
import java.util.HashSet;
import java.util.Set;public class HashSetExample {public static void main(String[] args) {Set<String> colors = new HashSet<>();// 添加元素colors.add("Red");colors.add("Green");colors.add("Blue");colors.add("Red"); // 重复元素,添加失败System.out.println("Colors: " + colors); // 顺序不确定,如 [Red, Blue, Green]System.out.println("Size: " + colors.size()); // 3// 检查元素boolean hasRed = colors.contains("Red");System.out.println("Contains Red: " + hasRed); // true// 删除元素colors.remove("Green");System.out.println("After remove: " + colors); // [Red, Blue]// 遍历System.out.println("Colors:");for (String color : colors) {System.out.println("- " + color);}}
}
3.2.2 哈希码与equals方法

HashSet依赖于对象的hashCode()equals()方法来确保唯一性。对于自定义对象,必须正确重写这两个方法。

import java.util.Objects;public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}// 重写equals方法@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;Person person = (Person) o;return age == person.age && Objects.equals(name, person.name);}// 重写hashCode方法@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + '}';}
}
public class HashSetWithCustomObject {public static void main(String[] args) {Set<Person> people = new HashSet<>();people.add(new Person("Alice", 25));people.add(new Person("Bob", 30));people.add(new Person("Alice", 25)); // 与第一个Alice相等,不会被添加System.out.println("People: " + people);// 输出:[Person{name='Alice', age=25}, Person{name='Bob', 30}]}
}

性能特点

  • 添加、删除、查找:平均O(1),最坏O(n)(哈希冲突严重时)。

3.3 LinkedHashSet:有序的HashSet

LinkedHashSet继承自HashSet,但它通过维护一个双向链表来记录元素的插入顺序。因此,它既保证了元素的唯一性,又保持了插入顺序。

3.3.1 基本使用
import java.util.LinkedHashSet;
import java.util.Set;public class LinkedHashSetExample {public static void main(String[] args) {Set<String> insertionOrdered = new LinkedHashSet<>();insertionOrdered.add("First");insertionOrdered.add("Second");insertionOrdered.add("Third");insertionOrdered.add("First"); // 重复,不添加System.out.println("Insertion ordered: " + insertionOrdered);// 输出:[First, Second, Third] - 保持插入顺序}
}

性能特点

  • HashSet基本相同,但略慢(需要维护链表)。
  • 空间开销:比HashSet大。

3.4 TreeSet:基于红黑树

TreeSet实现了SortedSet接口,使用红黑树(一种自平衡二叉搜索树)存储元素。它保证元素按照升序排列(或自定义比较器指定的顺序),并且不允许null值。

3.4.1 基本使用
import java.util.TreeSet;
import java.util.Set;public class TreeSetExample {public static void main(String[] args) {Set<Integer> sortedNumbers = new TreeSet<>();sortedNumbers.add(5);sortedNumbers.add(1);sortedNumbers.add(3);sortedNumbers.add(9);sortedNumbers.add(2);System.out.println("Sorted numbers: " + sortedNumbers);// 输出:[1, 2, 3, 5, 9] - 自动排序// 获取第一个和最后一个元素Integer first = ((TreeSet<Integer>) sortedNumbers).first();Integer last = ((TreeSet<Integer>) sortedNumbers).last();System.out.println("First: " + first + ", Last: " + last); // 1, 9// 获取子集Set<Integer> subset = ((TreeSet<Integer>) sortedNumbers).subSet(2, 7);System.out.println("Subset [2, 7): " + subset); // [2, 3, 5]// 获取头部和尾部集合Set<Integer> headSet = ((TreeSet<Integer>) sortedNumbers).headSet(4);Set<Integer> tailSet = ((TreeSet<Integer>) sortedNumbers).tailSet(4);System.out.println("Head set (<4): " + headSet); // [1, 2, 3]System.out.println("Tail set (>=4): " + tailSet); // [5, 9]}
}
3.4.2 自定义排序
import java.util.Comparator;
import java.util.TreeSet;public class CustomSortedSet {public static void main(String[] args) {// 按字符串长度排序TreeSet<String> lengthSorted = new TreeSet<>((s1, s2) -> Integer.compare(s1.length(), s2.length()));lengthSorted.add("Apple");lengthSorted.add("Fig");lengthSorted.add("Banana");lengthSorted.add("Kiwi");System.out.println("Length sorted: " + lengthSorted);// 输出:[Fig, Kiwi, Apple, Banana] (按长度:3,4,5,6)// 或者实现Comparator接口TreeSet<String> reverseAlphabetical = new TreeSet<>(Comparator.reverseOrder());reverseAlphabetical.add("Apple");reverseAlphabetical.add("Banana");reverseAlphabetical.add("Cherry");System.out.println("Reverse order: " + reverseAlphabetical);// 输出:[Cherry, Banana, Apple]}
}

性能特点

  • 添加、删除、查找O(log n)
  • 排序:自动维护有序性。

3.5 Set的遍历与操作

import java.util.*;public class SetOperations {public static void main(String[] args) {Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));// 并集 (Union)Set<Integer> union = new HashSet<>(set1);union.addAll(set2);System.out.println("Union: " + union); // [1, 2, 3, 4, 5, 6, 7, 8]// 交集 (Intersection)Set<Integer> intersection = new HashSet<>(set1);intersection.retainAll(set2);System.out.println("Intersection: " + intersection); // [4, 5]// 差集 (Difference)Set<Integer> difference = new HashSet<>(set1);difference.removeAll(set2);System.out.println("Difference (set1 - set2): " + difference); // [1, 2, 3]// 子集判断Set<Integer> subset = new HashSet<>(Arrays.asList(1, 2));boolean isSubset = set1.containsAll(subset);System.out.println("Is subset: " + isSubset); // true}
}

四、Map接口:键值对的映射

Map接口存储键值对(key-value pairs),其中键是唯一的。它不继承自Collection接口,但提供了集合视图(keySet(), values(), entrySet())来访问其内容。

4.1 Map的核心特性

  • 键的唯一性:每个键最多映射到一个值。
  • 值的可重复性:不同的键可以映射到相同的值。
  • 常用方法
    • put(K key, V value):添加或更新键值对。
    • get(Object key):根据键获取值,键不存在时返回null
    • remove(Object key):移除指定键的映射。
    • containsKey(Object key) / containsValue(Object value):检查是否包含键或值。
    • size() / isEmpty():获取大小和检查是否为空。
    • keySet():返回所有键的Set视图。
    • values():返回所有值的Collection视图。
    • entrySet():返回所有键值对的Set<Map.Entry<K,V>>视图。

4.2 HashMap:基于哈希表的Map

HashMapMap接口最常用的实现,基于哈希表。它提供最快的查找、添加和删除操作,但不保证映射的顺序。

4.2.1 基本使用
import java.util.HashMap;
import java.util.Map;public class HashMapExample {public static void main(String[] args) {Map<String, Integer> ages = new HashMap<>();// 添加键值对ages.put("Alice", 25);ages.put("Bob", 30);ages.put("Charlie", 35);ages.put("Alice", 26); // 更新Alice的年龄System.out.println("Ages: " + ages); // {Alice=26, Bob=30, Charlie=35}// 获取值Integer aliceAge = ages.get("Alice");System.out.println("Alice's age: " + aliceAge); // 26// 检查键boolean hasDavid = ages.containsKey("David");System.out.println("Has David: " + hasDavid); // false// 检查值boolean hasAge30 = ages.containsValue(30);System.out.println("Has age 30: " + hasAge30); // true// 删除键值对ages.remove("Bob");System.out.println("After remove Bob: " + ages); // {Alice=26, Charlie=35}// 遍历System.out.println("Entries:");for (Map.Entry<String, Integer> entry : ages.entrySet()) {System.out.println(entry.getKey() + " -> " + entry.getValue());}// 仅遍历键System.out.println("Keys:");for (String name : ages.keySet()) {System.out.println(name);}// 仅遍历值System.out.println("Values:");for (Integer age : ages.values()) {System.out.println(age);}}
}
4.2.2 处理null值

HashMap允许null键和null值。

ages.put(null, 100); // 允许null键
ages.put("David", null); // 允许null值Integer nullKeyAge = ages.get(null); // 返回100
Integer davidAge = ages.get("David"); // 返回null
Integer unknownAge = ages.get("Unknown"); // 也返回null

注意get()方法在键不存在或键映射到null值时都返回null。如果需要区分这两种情况,可以使用containsKey()

4.2.3 性能特点
  • 查找、添加、删除:平均O(1),最坏O(n)
  • 初始容量与负载因子:默认初始容量16,负载因子0.75。当元素数量超过容量 * 负载因子时,会进行扩容(rehashing)。

4.3 LinkedHashMap:有序的HashMap

LinkedHashMap继承自HashMap,通过维护一个双向链表来记录元素的插入顺序或访问顺序。

4.3.1 按插入顺序排序
import java.util.LinkedHashMap;
import java.util.Map;public class LinkedHashMapInsertionOrder {public static void main(String[] args) {Map<String, Integer> insertionOrdered = new LinkedHashMap<>();insertionOrdered.put("First", 1);insertionOrdered.put("Second", 2);insertionOrdered.put("Third", 3);System.out.println("Insertion ordered: " + insertionOrdered);// 输出:{First=1, Second=2, Third=3}}
}
4.3.2 按访问顺序排序(LRU缓存)
import java.util.LinkedHashMap;
import java.util.Map;public class LRUCacheExample {// 实现一个简单的LRU缓存static class LRUCache<K, V> extends LinkedHashMap<K, V> {private final int capacity;public LRUCache(int capacity) {// accessOrder=true 表示按访问顺序排序super(capacity, 0.75f, true);this.capacity = capacity;}@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {// 当元素数量超过容量时,移除最老的条目return size() > capacity;}}public static void main(String[] args) {LRUCache<String, Integer> cache = new LRUCache<>(3);cache.put("A", 1);cache.put("B", 2);cache.put("C", 3);System.out.println("Cache: " + cache); // {A=1, B=2, C=3}cache.get("A"); // 访问A,将其移到末尾System.out.println("After get A: " + cache); // {B=2, C=3, A=1}cache.put("D", 4); // 添加D,触发移除最老的BSystem.out.println("After put D: " + cache); // {C=3, A=1, D=4}}
}

4.4 TreeMap:基于红黑树的有序Map

TreeMap实现了SortedMap接口,使用红黑树存储键值对。它保证键按照升序排列(或自定义比较器指定的顺序)。

4.4.1 基本使用
import java.util.TreeMap;
import java.util.Map;public class TreeMapExample {public static void main(String[] args) {Map<String, Integer> sortedMap = new TreeMap<>();sortedMap.put("Banana", 5);sortedMap.put("Apple", 3);sortedMap.put("Cherry", 8);sortedMap.put("Date", 2);System.out.println("Sorted map: " + sortedMap);// 输出:{Apple=3, Banana=5, Cherry=8, Date=2} - 按键的字母顺序排序// 获取第一个和最后一个条目Map.Entry<String, Integer> firstEntry = sortedMap.firstEntry();Map.Entry<String, Integer> lastEntry = sortedMap.lastEntry();System.out.println("First: " + firstEntry); // Apple=3System.out.println("Last: " + lastEntry);   // Date=2// 获取子映射Map<String, Integer> subMap = sortedMap.subMap("Apple", "Date");System.out.println("Submap [Apple, Date): " + subMap); // {Apple=3, Banana=5, Cherry=8}// 获取头部和尾部映射Map<String, Integer> headMap = sortedMap.headMap("Cherry");Map<String, Integer> tailMap = sortedMap.tailMap("Cherry");System.out.println("Head map (<Cherry): " + headMap); // {Apple=3, Banana=5}System.out.println("Tail map (>=Cherry): " + tailMap); // {Cherry=8, Date=2}}
}
4.4.2 自定义排序
import java.util.Comparator;
import java.util.TreeMap;public class CustomSortedMap {public static void main(String[] args) {// 按值排序(需要自定义比较器)TreeMap<String, Integer> valueSorted = new TreeMap<>((k1, k2) -> {int valueCompare = Integer.compare(valueSorted.get(k1), valueSorted.get(k2));return valueCompare != 0 ? valueCompare : k1.compareTo(k2); // 如果值相等,按键排序});// 注意:上面的比较器在put时无法获取值,因此需要另一种方式// 更好的方式是使用值的排序,但TreeMap按键排序// 如果需要按值排序,可以将条目放入List并排序Map<String, Integer> unsorted = new HashMap<>();unsorted.put("A", 3);unsorted.put("B", 1);unsorted.put("C", 4);// 按值降序排序unsorted.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).forEach(entry -> System.out.println(entry.getKey() + "=" + entry.getValue()));// 输出:C=4, A=3, B=1}
}

性能特点

  • 查找、添加、删除O(log n)
  • 不允许null键(但允许null值,除非比较器支持null)。

4.5 Hashtable与Properties

HashtableHashMap的线程安全版本,其方法使用synchronized修饰。与HashMap不同,Hashtable不允许null键和null值。由于性能问题,现代开发中通常使用ConcurrentHashMap替代。

Properties继承自Hashtable,专门用于处理配置文件(.properties文件)。

import java.util.Properties;
import java.io.*;public class PropertiesExample {public static void main(String[] args) throws IOException {Properties props = new Properties();// 设置属性props.setProperty("database.url", "jdbc:mysql://localhost:3306/mydb");props.setProperty("database.username", "admin");props.setProperty("database.password", "secret");// 保存到文件try (FileOutputStream out = new FileOutputStream("config.properties")) {props.store(out, "Database Configuration");}// 从文件加载Properties loadedProps = new Properties();try (FileInputStream in = new FileInputStream("config.properties")) {loadedProps.load(in);}System.out.println("Loaded properties: " + loadedProps);}
}

4.6 Map的遍历方式

import java.util.*;public class MapTraversal {public static void main(String[] args) {Map<String, Integer> map = new HashMap<>();map.put("A", 1);map.put("B", 2);map.put("C", 3);// 1. 遍历entrySet(推荐,同时获取键和值)System.out.println("EntrySet:");for (Map.Entry<String, Integer> entry : map.entrySet()) {System.out.println(entry.getKey() + " -> " + entry.getValue());}// 2. 遍历keySetSystem.out.println("KeySet:");for (String key : map.keySet()) {System.out.println(key + " -> " + map.get(key));}// 3. 遍历valuesSystem.out.println("Values:");for (Integer value : map.values()) {System.out.println(value);}// 4. 使用IteratorSystem.out.println("Iterator (entrySet):");Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();while (iterator.hasNext()) {Map.Entry<String, Integer> entry = iterator.next();System.out.println(entry.getKey() + " -> " + entry.getValue());// iterator.remove(); // 如果需要在遍历中删除}// 5. Java 8 forEachSystem.out.println("forEach:");map.forEach((key, value) -> System.out.println(key + " -> " + value));// 6. Java 8 Stream APISystem.out.println("Stream API:");map.entrySet().stream().forEach(entry -> System.out.println(entry.getKey() + " -> " + entry.getValue()));}
}

五、集合的线程安全

Java集合框架中的大多数实现(如ArrayList, HashMap, HashSet)都不是线程安全的。在多线程环境中直接使用它们可能导致数据不一致或ConcurrentModificationException

5.1 同步包装器

Collections类提供了将非线程安全集合转换为线程安全集合的静态方法。

import java.util.*;public class SynchronizedCollections {public static void main(String[] args) {List<String> syncList = Collections.synchronizedList(new ArrayList<>());Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());Map<String, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());// 使用同步集合syncList.add("Item");syncSet.add("Element");syncMap.put("Key", 1);// 注意:遍历时需要手动同步synchronized (syncList) {for (String item : syncList) {System.out.println(item);}}}
}

5.2 并发集合

java.util.concurrent包提供了专门为高并发场景设计的集合类。

5.2.1 ConcurrentHashMap

ConcurrentHashMapHashMap的线程安全版本,通过分段锁(JDK 7)或CAS+synchronized(JDK 8+)实现高并发性能。

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ConcurrentHashMapExample {public static void main(String[] args) {ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();ExecutorService executor = Executors.newFixedThreadPool(10);// 模拟并发操作for (int i = 0; i < 1000; i++) {final int taskId = i;executor.submit(() -> {String key = "Task" + taskId % 10;map.merge(key, 1, Integer::sum); // 原子性地增加计数});}executor.shutdown();try {executor.awaitTermination(1, TimeUnit.MINUTES);} catch (InterruptedException e) {Thread.currentThread().interrupt();}System.out.println("Task counts: " + map);}
}
5.2.2 CopyOnWriteArrayList

CopyOnWriteArrayList在修改操作(add, set, remove)时创建底层数组的副本,适用于读多写少的场景。

import java.util.concurrent.CopyOnWriteArrayList;public class CopyOnWriteExample {public static void main(String[] args) {CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();list.add("A");list.add("B");list.add("C");// 遍历时可以安全地修改(因为遍历的是快照)for (String item : list) {System.out.println(item);if ("B".equals(item)) {list.add("D"); // 不会影响当前遍历}}System.out.println("Final list: " + list); // [A, B, C, D]}
}

六、集合的最佳实践与总结

6.1 选择合适的集合

需求推荐集合
需要有序且允许重复ArrayList
频繁在中间插入/删除LinkedList
不允许重复元素HashSet
保持插入顺序且无重复LinkedHashSet
需要排序的元素TreeSet
键值对映射,高性能HashMap
保持插入顺序的键值对LinkedHashMap
需要排序的键值对TreeMap
线程安全的MapConcurrentHashMap
读多写少的ListCopyOnWriteArrayList

6.2 最佳实践

  1. 使用接口而非具体实现List<String> list = new ArrayList<>();
  2. 指定泛型类型:避免原始类型。
  3. 初始化时指定容量:减少扩容开销。
  4. 合理使用Collections工具类:如unmodifiable, synchronized, emptyList等。
  5. 优先使用Java 8+的Stream API:进行复杂的集合操作。
  6. 注意线程安全:在多线程环境中选择合适的并发集合。

6.3 总结

Java集合框架是Java编程的基石之一。ListSetMap三大接口及其丰富的实现类,为各种数据存储和操作需求提供了完美的解决方案。理解它们的特性、性能差异和适用场景,能够帮助你编写出高效、健壮的Java代码。通过不断实践和深入学习,你将能够灵活运用这些工具,应对复杂的编程挑战。

http://www.dtcms.com/a/314226.html

相关文章:

  • 十字滑台:精密制造的“心脏“如何跳动?
  • 耘瞳科技国产化点云处理软件,开启智能化三维测量新时代
  • [机器学习]01-构建简单的贝叶斯分类器
  • 抓包相关知识学习
  • 项目复盘:Arena Of Furnace
  • vtkSSAAPass代码解析
  • [自动化Adapt] 回放策略 | AI模型驱动程序
  • Python异常捕获全指南
  • 智慧泵房赋能二次供水互联网化:物联网驱动下的全场景解决方案
  • Solidity全局变量与安全实践指南
  • Linux 文件与目录属性管理总结
  • 设备能力指标(CP/CPK)
  • C盘空间清理
  • JVM学习日记(十六)Day16——性能监控与调优(三)
  • AgxOrin平台JetPack5.x版本fix multi-cam race condition 补丁
  • 【Conda】常用指令操作
  • 机器学习——决策树(DecisionTree)+ 过采样 + 交叉验证 案例:电信客户流失数据
  • VAE学习笔记
  • Linux 网络深度剖析:传输层协议 UDP/TCP 原理详解
  • 【STM32】GPIO的输入输出
  • 正点原子STM32MP257开发板移植ubuntu24.04根文件系统(带桌面版)
  • Android的UI View是如何最终绘制成一帧显示在手机屏幕上?
  • Android Espresso 测试框架深度解析:从入门到精通
  • imx6ull-驱动开发篇8——设备树常用 OF 操作函数
  • 力扣热题100——哈希表
  • 大模型×垂直领域:预算、时间、空间三重夹击下的生存法则
  • 基于ensp的防火墙安全策略及认证策略综合实验
  • Flink CDC 介绍
  • PHP-分支语句、while循环、for循环
  • 深入理解Spring中的循环依赖及解决方案