nav_path: java-collections


Java集合类的主要目的是创建一个简单的、高性能的数据结构操作框架。Java的集合框架围绕一组标准接口设计,根接口是数据集合(Collection)与键值映射表(Map),并基于这两个接口派生了多个子接口,实现包括数组、哈希表、集合等数据结构。

以下是Collection接口派生的主要集合类:

  1. 容器接口:如CollectionMapSet等。
  2. 实现类:具体实现容器的数据结构,如HashSetArrayList等。
  3. 相应算法:配套数据结构的算法,如排序、搜索等。

画板

Collection接口

Collection接口主要定义了一些通用的方法,供后续数据结构实现:

  1. public interface Collection<E> extends Iterable<E> {
  2. int size(); // 集合内元素个数
  3. boolean isEmpty(); // 集合是否为空
  4. boolean contains(Object o); // 是否包含某个元素
  5. Iterator<E> iterator(); // 返回迭代器
  6. Object[] toArray(); // 转换为数组
  7. <T> T[] toArray(T[] a); // 转换为指定类型数组
  8. boolean add(E e); // 添加元素
  9. boolean remove(Object o); // 移除元素
  10. boolean containsAll(Collection<?> c); // 是否包含另一集合的所有元素
  11. boolean addAll(Collection<? extends E> c); // 添加另一集合的所有元素
  12. boolean removeAll(Collection<?> c); // 移除另一集合的所有元素
  13. default boolean removeIf(Predicate<? super E> filter) { // 条件移除
  14. // 实现略
  15. }
  16. boolean retainAll(Collection<?> c); // 保留另一集合的所有元素
  17. void clear(); // 清空集合
  18. boolean equals(Object o); // 判断相等
  19. int hashCode(); // 计算哈希码
  20. default Spliterator<E> spliterator() { // 分割为多个迭代器
  21. return Spliterators.spliterator(this, 0);
  22. }
  23. default Stream<E> stream() { // 流处理
  24. return StreamSupport.stream(spliterator(), false);
  25. }
  26. default Stream<E> parallelStream() { // 并行流处理
  27. return StreamSupport.stream(spliterator(), true);
  28. }
  29. }

Iterable与Iterator

Iterable接口

Iterable接口主要用于遍历集合:

  1. public interface Iterable<T> {
  2. Iterator<T> iterator();
  3. default void forEach(Consumer<? super T> action) {
  4. Objects.requireNonNull(action);
  5. for (T t : this) {
  6. action.accept(t);
  7. }
  8. }
  9. default Spliterator<T> spliterator() {
  10. return Spliterators.spliteratorUnknownSize(iterator(), 0);
  11. }
  12. }

Iterator接口

Iterator接口有两个主要方法:hasNextnext,前者查询是否还有下一个元素,后者返回下一个元素。不同数据结构对这两个方法的实现不一致。

  1. public interface Iterator<E> {
  2. boolean hasNext(); // 是否有下一个元素
  3. E next(); // 返回下一个元素
  4. default void remove() { // 移除元素
  5. throw new UnsupportedOperationException("remove");
  6. }
  7. default void forEachRemaining(Consumer<? super E> action) { // 对剩余元素进行操作
  8. Objects.requireNonNull(action);
  9. while (hasNext()) {
  10. action.accept(next());
  11. }
  12. }
  13. }

Collection的三个子接口

List接口

List是一个元素可重复的有序集合,可以通过下标操作集合内元素,增加了一些自定义的方法:

  1. public interface List<E> extends Collection<E> {
  2. void sort(Comparator<? super E> c); // 排序
  3. E get(int index); // 获取指定下标的元素
  4. E set(int index, E element); // 替换指定下标的元素
  5. int indexOf(Object o); // 返回第一次出现的下标
  6. int lastIndexOf(Object o); // 返回最后一次出现的下标
  7. List<E> subList(int fromIndex, int toIndex); // 提取子List
  8. ListIterator<E> listIterator(); // 返回迭代器
  9. ListIterator<E> listIterator(int index); // 返回指定位置的迭代器
  10. }

ListIterator接口

ListIterator继承自Iterator,在其基础上增加了方法,便于List的遍历和使用:

  1. public interface ListIterator<E> extends Iterator<E> {
  2. boolean hasNext(); // 是否有下一个元素
  3. E next(); // 返回下一个元素
  4. boolean hasPrevious(); // 是否有上一个元素
  5. E previous(); // 返回上一个元素
  6. int nextIndex(); // 下一个元素的索引
  7. int previousIndex(); // 上一个元素的索引
  8. void remove(); // 移除元素
  9. void set(E e); // 替换元素
  10. void add(E e); // 添加元素
  11. }

Vector类

Vector的底层是元素数组,具有以下重要属性:

  1. protected Object[] elementData; // 元素数组
  2. protected int elementCount; // 当前数组中的元素个数
  3. protected int capacityIncrement; // 扩容时容量增加数量

在插入元素时,先判定当前空间是否足够:

  1. public synchronized boolean add(E e) {
  2. modCount++;
  3. ensureCapacityHelper(elementCount + 1);
  4. elementData[elementCount++] = e;
  5. return true;
  6. }

扩容方法:

  1. private void grow(int minCapacity) {
  2. int oldCapacity = elementData.length;
  3. int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
  4. if (newCapacity - minCapacity < 0)
  5. newCapacity = minCapacity;
  6. if (newCapacity - MAX_ARRAY_SIZE > 0)
  7. newCapacity = hugeCapacity(minCapacity);
  8. elementData = Arrays.copyOf(elementData, newCapacity);
  9. }

Vector是线程安全的,采用sychronized对成员函数加锁。

Stack类

Stack继承自Vector,在基类的基础上实现了pushpoppeek等方法,实现了栈的功能,也是线程安全的。

  1. public class Stack<E> extends Vector<E> {
  2. public E push(E item) {
  3. addElement(item);
  4. return item;
  5. }
  6. public synchronized E pop() {
  7. E obj;
  8. int len = size();
  9. obj = peek();
  10. removeElementAt(len - 1);
  11. return obj;
  12. }
  13. public synchronized E peek() {
  14. int len = size();
  15. if (len == 0)
  16. throw new EmptyStackException();
  17. return elementAt(len - 1);
  18. }
  19. public boolean empty() {
  20. return size() == 0;
  21. }
  22. public synchronized int search(Object o) {
  23. int i = lastIndexOf(o);
  24. if (i >= 0) {
  25. return size() - i;
  26. }
  27. return -1;
  28. }
  29. }

ArrayList类

ArrayList的底层也是一个自动扩容数组,扩容为2倍(无法修改),是线程不安全的,性能较优:

  1. public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
  2. private static final int DEFAULT_CAPACITY = 10;
  3. private static final Object[] EMPTY_ELEMENTDATA = {};
  4. private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  5. transient Object[] elementData; // non-private to simplify nested class access
  6. private int size;
  7. public boolean add(E e) {
  8. ensureCapacityInternal(size + 1); // Increments modCount!!
  9. elementData[size++] = e;
  10. return true;
  11. }
  12. }

LinkedList类

LinkedListList接口的实现类,也实现了Deque接口,可以作为双端队列使用:

  1. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
  2. transient int size = 0;
  3. transient Node<E> first;
  4. transient Node<E> last;
  5. private static class Node<E> {
  6. E item;
  7. Node<E> next;
  8. Node<E> prev;
  9. Node(Node<E> prev, E element, Node<E> next) {
  10. this.item = element;
  11. this.next = next;
  12. this.prev = prev;
  13. }
  14. }
  15. public void addFirst(E e) {
  16. linkFirst(e);
  17. }
  18. private void linkFirst(E e) {
  19. final Node<E> f = first;
  20. final Node<E> newNode = new Node<>(null, e, f);
  21. first = newNode;
  22. if (f == null)
  23. last = newNode;
  24. else
  25. f.prev = newNode;
  26. size++;
  27. modCount++;
  28. }
  29. }

ArrayList与LinkedList性能对比

  • ArrayList:随机访问效率高,插入、删除效率低。
  • **Linked

Set接口概述

Set是一个不允许重复元素的集合,接口定义与Collection几乎一样。其特点在于集合内部不能存在相同的元素,在第二次加入相同的元素时会提示错误。

HashSet类

HashSet实质上是一个HashMap,在插入元素时,以元素作为键,并使用一个虚拟对象作为值:

  1. public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
  2. private transient HashMap<E,Object> map;
  3. private static final Object PRESENT = new Object();
  4. public HashSet() {
  5. map = new HashMap<>();
  6. }
  7. public boolean add(E e) {
  8. return map.put(e, PRESENT) == null;
  9. }
  10. }

EnumSet类

EnumSet是一个基于枚举类型的集合,可以通过静态工厂方法获取实例:

  1. public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E> implements Cloneable, java.io.Serializable {
  2. public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
  3. Enum<?>[] universe = getUniverse(elementType);
  4. if (universe == null)
  5. throw new ClassCastException(elementType + " not an enum");
  6. if (universe.length <= 64)
  7. return new RegularEnumSet<>(elementType, universe);
  8. else
  9. return new JumboEnumSet<>(elementType, universe);
  10. }
  11. }

使用示例:

  1. public static <E extends Enum<E>> EnumSet<E> of(E e) {
  2. EnumSet<E> result = EnumSet.noneOf(e.getDeclaringClass());
  3. result.add(e);
  4. return result;
  5. }

TreeSet类

TreeSet基于TreeMap实现,具有排序功能。


Queue接口

Queue接口概述

Queue接口提供队列操作方法,包括出队和入队操作:

  1. public interface Queue<E> extends Collection<E> {
  2. boolean add(E e); // 将指定元素加入队列
  3. boolean offer(E e); // 提供元素
  4. E remove(); // 获取并移除队列头
  5. E poll(); // 获取并移除队列头,返回null如果队列为空
  6. E element(); // 获取队列头
  7. E peek(); // 获取队列头,返回null如果队列为空
  8. }

ArrayDeque类

ArrayDeque是基于数组实现的双端队列,扩容方法如下:

  1. private void doubleCapacity() {
  2. assert head == tail;
  3. int p = head;
  4. int n = elements.length;
  5. int r = n - p;
  6. int newCapacity = n << 1;
  7. if (newCapacity < 0)
  8. throw new IllegalStateException("Sorry, deque too big");
  9. Object[] a = new Object[newCapacity];
  10. System.arraycopy(elements, p, a, 0, r);
  11. System.arraycopy(elements, 0, a, r, p);
  12. elements = a;
  13. head = 0;
  14. tail = n;
  15. }

PriorityQueue类

PriorityQueue根据队列中元素大小进行排序,调用peekpoll时,返回最大或最小的元素。插入方法如下:

  1. private void siftUpComparable(int k, E x) {
  2. Comparable<? super E> key = (Comparable<? super E>) x;
  3. while (k > 0) {
  4. int parent = (k - 1) >>> 1;
  5. Object e = queue[parent];
  6. if (key.compareTo((E) e) >= 0)
  7. break;
  8. queue[k] = e;
  9. k = parent;
  10. }
  11. queue[k] = key;
  12. }

Map接口

Map接口概述

Map接口提供通过键值对存储、查询和删除数据的方法:

  1. public interface Map<K,V> {
  2. int size();
  3. boolean isEmpty();
  4. boolean containsKey(Object key);
  5. boolean containsValue(Object value);
  6. V get(Object key);
  7. V put(K key, V value);
  8. V remove(Object key);
  9. void putAll(Map<? extends K, ? extends V> m);
  10. void clear();
  11. Set<K> keySet();
  12. Collection<V> values();
  13. Set<Map.Entry<K, V>> entrySet();
  14. default V putIfAbsent(K key, V value) { ... }
  15. default boolean replace(K key, V oldValue, V newValue) { ... }
  16. default V replace(K key, V value) { ... }
  17. }

Entry接口

Map中,键值对用Entry对象存储:

  1. interface Entry<K,V> {
  2. K getKey();
  3. V getValue();
  4. V setValue(V value);
  5. boolean equals(Object o);
  6. int hashCode();
  7. static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
  8. return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getKey().compareTo(c2.getKey());
  9. }
  10. static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
  11. return (Comparator<Map.Entry<K, V>> & Serializable) (c1, c2) -> c1.getValue().compareTo(c2.getValue());
  12. }
  13. }

HashMap类

HashMap采用数组+链表(红黑树)结构,使用哈希算法确定键值对的存放位置。以下是哈希表的插入逻辑:

  1. public V put(K key, V value) {
  2. return putVal(hash(key), key, value, false, true);
  3. }
  4. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
  5. Node<K,V>[] tab; Node<K,V> p; int n, i;
  6. if ((tab = table) == null || (n = tab.length) == 0)
  7. n = (tab = resize()).length;
  8. if ((p = tab[i = (n - 1) & hash]) == null)
  9. tab[i] = newNode(hash, key, value, null);
  10. else {
  11. Node<K,V> e; K k;
  12. if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
  13. e = p;
  14. else if (p instanceof TreeNode)
  15. e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
  16. else {
  17. for (int binCount = 0; ; ++binCount) {
  18. if ((e = p.next) == null) {
  19. p.next = newNode(hash, key, value, null);
  20. if (binCount >= TREEIFY_THRESHOLD - 1)
  21. treeifyBin(tab, hash);
  22. break;
  23. }
  24. if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
  25. break;
  26. p = e;
  27. }
  28. }
  29. if (e != null) {
  30. V oldValue = e.value;
  31. if (!onlyIfAbsent || oldValue == null)
  32. e.value = value;
  33. afterNodeAccess(e);
  34. return oldValue;
  35. }
  36. }
  37. ++modCount;
  38. if (++size > threshold)
  39. resize();
  40. afterNodeInsertion(evict);
  41. return null;
  42. }

LinkedHashMap类

LinkedHashMapHashMap基础上维护了一个双向链表,按插入顺序迭代:

  1. public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V> {
  2. transient LinkedHashMap.Entry<K,V> head;
  3. transient LinkedHashMap.Entry<K,V> tail;
  4. public void afterNodeInsertion(boolean evict) {
  5. LinkedHashMap.Entry<K,V> first;
  6. if (accessOrder && (first = head) != null) {
  7. LinkedHashMap.Entry<K,V> last;
  8. LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)last;
  9. first.before = p;
  10. if (p == null)
  11. first = p;
  12. else
  13. p.after = first;
  14. head = last;
  15. if (tail == null)
  16. tail = last;
  17. }
  18. }
  19. }

TreeMap类

TreeMap基于红黑树实现,按自然顺序或指定的Comparator排序:

  1. public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
  2. private final Comparator<? super K> comparator;
  3. private transient Entry<K,V> root;
  4. public V put(K key, V value) {
  5. Entry<K,V> t = root;
  6. if (t == null) {
  7. compare(key, key); // type (and possibly null) check
  8. root = new Entry<>(key, value, null);
  9. size = 1;
  10. modCount++;
  11. return null;
  12. }
  13. int cmp;
  14. Entry<K,V> parent;
  15. Comparator<? super K> cpr = comparator;
  16. if (cpr != null) {
  17. do {
  18. parent = t;
  19. cmp = cpr.compare(key, t.key);
  20. if (cmp < 0)
  21. t = t.left;
  22. else if (cmp > 0)
  23. t = t.right;
  24. else
  25. return t.setValue(value);
  26. } while (t != null);
  27. }
  28. else {
  29. if (key == null)
  30. throw new NullPointerException();
  31. Comparable<? super K> k = (Comparable<? super K>) key;
  32. do {
  33. parent = t;
  34. cmp = k.compareTo(t.key);
  35. if (cmp < 0)
  36. t = t.left;
  37. else if (cmp > 0)
  38. t = t.right;
  39. else
  40. return t.setValue(value);
  41. } while (t != null);
  42. }
  43. Entry<K,V> e = new Entry<>(key, value, parent);
  44. if (cmp < 0)
  45. parent.left = e;
  46. else
  47. parent.right = e;
  48. fixAfterInsertion(e);
  49. size++;
  50. modCount++;
  51. return null;
  52. }
  53. // 其他方法省略
  54. }

判定相等

  • 判定key相等:通过compareTo()方法返回0,认为两个key相等。
  • 判定value相等:通过equals()方法返回true,认为两个value相等。

TreeMap类的总结

  • TreeMap在插入、删除和查询时的时间复杂度均为O(log(n))
  • 适用于需要按顺序存储键值对的场景。

WeakHashMap类

WeakHashMapHashMap相似,其特点在于Entry继承了WeakReference(弱引用),具备弱引用特性。当某个键值对不再被其他对象引用时,无论内存是否够用,GC都会对其进行回收。

  1. public class WeakHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
  2. private final Map<K,WeakReference<V>> map = new HashMap<>();
  3. public V put(K key, V value) {
  4. WeakReference<V> ref = new WeakReference<>(value);
  5. return map.put(key, ref);
  6. }
  7. public V get(Object key) {
  8. WeakReference<V> ref = map.get(key);
  9. return (ref == null) ? null : ref.get();
  10. }
  11. // 其他方法省略
  12. }

示例:

  1. public class WeakHashMapExample {
  2. public static void main(String[] args) {
  3. String a = new String("a");
  4. String b = new String("b");
  5. Map<String, String> weakmap = new WeakHashMap<>();
  6. weakmap.put(a, "aaa");
  7. weakmap.put(b, "bbb");
  8. a = null; // 将字符串对象“a”的引用取消
  9. System.gc(); // 触发GC
  10. Iterator<Map.Entry<String, String>> iter = weakmap.entrySet().iterator();
  11. while (iter.hasNext()) {
  12. Map.Entry<String, String> entry = iter.next();
  13. System.out.println("weakmap: " + entry.getKey() + ": " + entry.getValue());
  14. // 只打印出 weakmap: b: bbb
  15. }
  16. }
  17. }

WeakHashMap的总结

  • 适用于缓存,可以及时回收不需要的键值对。

IdentityHashMap类

IdentityHashMap的主要区别在于:

  1. 没有使用红黑树
  2. 比较两个Key是否相同时,采用的是引用相同(reference-equality),而不是对象相同(object-equality)
  1. public class IdentityHashMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
  2. transient Object[] table;
  3. public V put(K key, V value) {
  4. int hash = System.identityHashCode(key);
  5. int index = hash & (table.length - 1);
  6. for (int i = index; i < table.length; i++) {
  7. if (table[i] == key || (table[i] == null && table[i + 1] == null)) {
  8. V oldValue = (V) table[i + 1];
  9. table[i + 1] = value;
  10. return oldValue;
  11. }
  12. }
  13. // 其他实现略
  14. }
  15. public V get(Object key) {
  16. int hash = System.identityHashCode(key);
  17. int index = hash & (table.length - 1);
  18. for (int i = index; i < table.length; i++) {
  19. if (table[i] == key) {
  20. return (V) table[i + 1];
  21. }
  22. }
  23. return null;
  24. }
  25. }

IdentityHashMap的总结

  • 适用于需要引用相同的场景。
  • 采用==进行比较,而不是equals()

总结

Map实现类的性能分析及适用场景

  • HashMap:快速查询设计,适用于一般应用场景。
  • LinkedHashMap:维护插入顺序,适用于需要按插入顺序遍历的场景。
  • TreeMap:按自然顺序排序,适用于需要按顺序存储键值对的场景。
  • WeakHashMap:具备弱引用特性,适用于缓存场景。
  • IdentityHashMap:采用引用相同进行比较,适用于需要引用相同的场景。

如何选择

  • 需要快速插入、删除元素:使用LinkedList
  • 需要快速随机访问元素:使用ArrayList
  • 单线程环境或单线程操作:使用非同步类(如ArrayList)。
  • 多线程环境且多个线程操作:使用同步类(如Vector)。
  • 需要按插入顺序遍历:使用LinkedHashMap
  • 需要按自然顺序存储键值对:使用TreeMap