Java 集合框架
集合框架概述
Java 集合框架提供了一套性能优良、使用方便的接口和类,位于 java.util 包中。
主要接口层次
Collection
├── List
├── Set
└── Queue
Map (独立接口)
List 接口
ArrayList
javaimport java.util.ArrayList; import java.util.List; public class ArrayListExample { public static void main(String[] args) { List<String> list = new ArrayList<>(); // 添加元素 list.add("Java"); list.add("Python"); list.add("Go"); // 访问元素 System.out.println(list.get(0)); // Java // 遍历 for (String language : list) { System.out.println(language); } // 使用 Stream API list.stream() .filter(lang -> lang.startsWith("J")) .forEach(System.out::println); } }
LinkedList
javaimport java.util.LinkedList; import java.util.List; public class LinkedListExample { public static void main(String[] args) { LinkedList<String> linkedList = new LinkedList<>(); linkedList.add("First"); linkedList.add("Second"); linkedList.addFirst("New First"); linkedList.addLast("Last"); System.out.println(linkedList.getFirst()); // New First System.out.println(linkedList.getLast()); // Last } }
Set 接口
HashSet
javaimport java.util.HashSet; import java.util.Set; public class HashSetExample { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("Apple"); set.add("Banana"); set.add("Apple"); // 重复元素,不会被添加 System.out.println(set.size()); // 2 System.out.println(set.contains("Apple")); // true } }
TreeSet
javaimport java.util.TreeSet; import java.util.Set; public class TreeSetExample { public static void main(String[] args) { Set<Integer> treeSet = new TreeSet<>(); treeSet.add(5); treeSet.add(2); treeSet.add(8); treeSet.add(1); // TreeSet 会自动排序 for (Integer num : treeSet) { System.out.println(num); // 1, 2, 5, 8 } } }
Map 接口
HashMap
javaimport java.util.HashMap; import java.util.Map; public class HashMapExample { public static void main(String[] args) { Map<String, Integer> map = new HashMap<>(); map.put("Alice", 25); map.put("Bob", 30); map.put("Charlie", 28); // 获取值 System.out.println(map.get("Alice")); // 25 // 遍历 for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); } // 使用 computeIfAbsent map.computeIfAbsent("David", k -> 35); } }
ConcurrentHashMap
javaimport java.util.concurrent.ConcurrentHashMap; import java.util.Map; public class ConcurrentHashMapExample { public static void main(String[] args) { Map<String, Integer> concurrentMap = new ConcurrentHashMap<>(); // 线程安全的操作 concurrentMap.put("Key1", 100); concurrentMap.putIfAbsent("Key1", 200); // 不会替换 System.out.println(concurrentMap.get("Key1")); // 100 } }
集合工具类 Collections
javaimport java.util.*; public class CollectionsExample { public static void main(String[] args) { List<Integer> list = Arrays.asList(3, 1, 4, 1, 5, 9); // 排序 Collections.sort(list); System.out.println("Sorted: " + list); // 反转 Collections.reverse(list); System.out.println("Reversed: " + list); // 查找 int index = Collections.binarySearch(list, 4); System.out.println("Index of 4: " + index); // 创建不可修改的集合 List<Integer> unmodifiableList = Collections.unmodifiableList(list); // unmodifiableList.add(10); // 会抛出 UnsupportedOperationException } }
常见面试题
1. ArrayList 和 LinkedList 的区别
| 特性 | ArrayList | LinkedList |
|---|---|---|
| 底层实现 | 动态数组 | 双向链表 |
| 随机访问 | O(1) | O(n) |
| 插入/删除 | O(n) | O(1) |
| 内存占用 | 较少 | 较多(需要存储前后指针) |
2. HashMap 的工作原理
java// HashMap 的 put 方法简化版 public V put(K key, V value) { // 1. 计算 key 的 hash 值 int hash = hash(key); // 2. 计算数组下标 int index = (n - 1) & hash; // 3. 处理哈希冲突(链表或红黑树) // ... // 4. 如果 key 已存在,更新值;否则添加新节点 // ... // 5. 检查是否需要扩容 if (++size > threshold) { resize(); } }
3. 实现 LRU 缓存
javaimport java.util.LinkedHashMap; import java.util.Map; class LRUCache<K, V> extends LinkedHashMap<K, V> { private final int capacity; public LRUCache(int capacity) { super(capacity, 0.75f, true); this.capacity = capacity; } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { return size() > capacity; } public static void main(String[] args) { LRUCache<Integer, String> cache = new LRUCache<>(3); cache.put(1, "A"); cache.put(2, "B"); cache.put(3, "C"); cache.get(1); // 访问 1,使其成为最近使用的 cache.put(4, "D"); // 这会移除 2(最久未使用) System.out.println(cache); // {3=C, 1=A, 4=D} } }
最佳实践
- 选择合适的集合类型:根据使用场景选择
- 使用泛型:避免类型转换错误
- 注意线程安全:多线程环境下使用并发集合
- 初始化容量:对于已知大小的集合,指定初始容量
- 使用不可变集合:当集合不需要修改时