一、概述
JAVA所有集合类都位于java.util包下,主要由两个接口派生而出:Collection 和 Map。
- Collection是java集合层级的根接口。一个集合代表一组对象,这些对象即为它的元素。Java平台不提供这个接口任何直接的实现。其中的子接口
Set是一个无序不重复的集合,无法按索引取出指定的元素。而子接口List是一个有序可重复集合,你可以通过它的索引来访问任何元素,List更像长度动态变换的数组。 - Map是一个以键值对保存数据的集合,key不可以重复(如果重复则新key的值会覆盖旧key的值),value可重复。
二、Collection 接口
1. Set 接口
1.1 HashSet
HashSet 的底层是基于 HashMap 来实现的,使用其 key 来存储集合元素,对应的velue全部是空的 Object 对象,从底层实现来看不难得出这是一个
无序且不允许有重复元素的集合。由于主要是基于 HashMap 来实现,所以这个集合本身的复杂度并没有多少,基本都是调用 HashMap 的API。
主要特性:
非线程安全,无序,不重复,元素可null
应用场景:
一般情况下,可以考虑用来进行元素的去重。
主要实现:
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
// 底层采用 HashMap 进行数据存储
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
...
}
添加元素: 重点看注释这句:If this set already contains the element, the call leaves the set unchanged and returns 如果此集合已包含元素,则调用将离开集合保持不变并返回
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
删除元素:
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
* if this set contains such an element. Returns <tt>true</tt> if
* this set contained the element (or equivalently, if this set
* changed as a result of the call). (This set will not contain the
* element once the call returns.)
*
* @param o object to be removed from this set, if present
* @return <tt>true</tt> if the set contained the specified element
*/
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
1.1.1 LinkedHashSet
LinkedHashSet 的UML图:
LinkedHashSet 也是 Set 集合的一个实现,具有 Set 集合不重复的特点,同时具有可预测的迭代顺序,也就是我们插入的顺序。
// 自身的构造方法
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
// 调用的父类 HashSet 构造方法 👇👇👇
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
由此可见其有序性是基于 LinkedHashMap 的实现。
主要特性:
非线程安全,有序,不重复,元素可null
应用场景:
相较于 HashSet 来说,LinkedHashSet 增加了顺序性,所以可以用在要求迭代顺序和插入顺序一致的场景。
主要实现:
参考 HashSet 。
1.2 TreeSet
TreeSet 的UML图:
TreeSet 是 SortedSet 接口实现类,是一种排序集合,该对象中只能添加一种类型元素,否则,会抛出java.lang.ClassCastException异常;它采用
红黑树这种数据结构存储集合元素。
主要特性:
非线程安全,有序,不重复,元素非null
应用场景:
需要快速查找的有序集合的时候可以使用。
主要实现:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
/**
* The backing map. 这个NavigableMap接口提供了在映射条目之间导航的功能
*/
private transient NavigableMap<E,Object> m;
// Dummy value to associate with an Object in the backing Map
// 要与 map 图中的对象关联的虚拟值,在add方法中设置的固定value值
private static final Object PRESENT = new Object();
...
}
添加元素:
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element {@code e} to this set if
* the set contains no element {@code e2} such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns {@code false}.
...
*/
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
删除元素:
/**
* Removes the specified element from this set if it is present.
* More formally, removes an element {@code e} such that
* <tt>(o==null ? e==null : o.equals(e))</tt>,
* if this set contains such an element. Returns {@code true} if
* this set contained the element (or equivalently, if this set
* changed as a result of the call). (This set will not contain the
* element once the call returns.)
...
*/
public boolean remove(Object o) {
return m.remove(o)==PRESENT;
}
2. List 接口
2.1 ArrayList
ArrayList 的UML图:
ArrayList 可以看作是存储空间可变的数组。
主要特性:
非线程安全,有序,可重复,元素可null,查询快但增删慢
应用场景:
在查询多增删少的场景下可以使用。
主要实现:
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity. 默认的容量大小为10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances. 用于空实例的共享空数组实例
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 用于默认大小的空实例的共享空数组实例。
* 我们将其与EMPTY_ELEMENTDATA区分开来,以了解当添加第一个元素
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
添加元素:
/**
* Appends the specified element to the end of this list.
* 追加指定的元素到集合的末尾
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!重要检查点
elementData[size++] = e;
return true;
}
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
* 向指定位置插入元素,如果当前指定的位置已经有元素了,则将该位置之后的所有元素往后移,然后将新元素
* 放置到指定位置上。
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!重要检查点
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
删除元素:
/**
* Removes the element at the specified position in this list.
* Shifts any subsequent elements to the left (subtracts one from their
* indices).
* 删除指定位置的元素,然后将该位置之后的元素统一向左移动一个位置。
* @param index the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
...
}
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
* 删除指定元素。如果该元素存在的话,删除遍历到的第一个出现的匹配值,如果不存在则不会有什么影响。
* 通常情况下会删除集合中所有匹配值中索引值最小的一个。
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
public boolean remove(Object o) {
...
}
2.2 LinkedList
LinkedList 的UML图
LinkedList 的底层采用
双向链表来存储数据,所以插入删除效率高,但是查找效率较低。另外它还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。
主要特性:
非线程安全,有序,可重复,元素可null,增删快但查询慢
应用场景:
与ArrayList 相对应的,在查询少增删多的场景下可以使用。
主要实现:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient int size = 0;
/**
* Pointer to first node. 前驱元素
* 这里的 transient 关键字的主要作用就是让某些被transient关键字修饰的成员属性变量不被序列化
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.后继元素
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
添加元素:
/**
* Appends the specified element to the end of this list.
* 将指定元素添加至集合的末尾。
* <p>This method is equivalent to {@link #addLast}.
* 该方法几乎等同于 addLast 方法
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
删除元素:
/**
* Retrieves and removes the head (first element) of this list.
* 从链表头部删除元素
* @return the head of this list
* @throws NoSuchElementException if this list is empty
* @since 1.5
*/
public E remove() {
return removeFirst();
}
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
* 按指定的索引位置删除元素
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
...
三、Map 接口
3.1 HashMap
作为Doug Lea (李二狗)老爷子的作品,常常是面试的重灾区,没办法,谁让人家设计的优秀呢?🤭
HashMap 最早在 JDK 1.2 中就出现了,底层是基于
散列算法实现,源码的复杂度也是挺高的,值得细细品读。在JDK1.8中涉及到的:散列表实现、扰动函数、初始化容量、负载因子、扩容元素拆分、链表树化、红黑树等等实现后续章节专门来讲,欢迎持续关注😄。
主要特性:
非线程安全,无序,可重复,元素可null
应用场景:
在需要存储键值对的场景下适合使用,比如map接参大法。
主要实现:
添加元素:
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
* 向集合中插入指定的key和value键值对,如果key已经存在,则新的value值会覆盖旧值。
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
...
获取元素:
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
*
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code (key==null ? k==null :
* key.equals(k))}, then this method returns {@code v}; otherwise
* it returns {@code null}. (There can be at most one such mapping.)
*
* <p>A return value of {@code null} does not <i>necessarily</i>
* indicate that the map contains no mapping for the key; it's also
* possible that the map explicitly maps the key to {@code null}.
* The {@link #containsKey containsKey} operation may be used to
* distinguish these two cases.
*
* @see #put(Object, Object)
*/
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
删除元素:
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key key whose mapping is to be removed from the map
* @return the previous value associated with <tt>key</tt>, or
* <tt>null</tt> if there was no mapping for <tt>key</tt>.
* (A <tt>null</tt> return can also indicate that the map
* previously associated <tt>null</tt> with <tt>key</tt>.)
*/
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
3.2 ConcurrentHashMap
主要特性:
线程安全,无序,可重复,元素不可null
应用场景:
在多线程环境下,使用 HashMap 进行 put 操作时存在丢失数据的情况,为了避免这种 bug 的隐患,建议使用ConcurrentHashMap 代替 HashMap。
主要实现:
添加元素:
/**
* Maps the specified key to the specified value in this table.
* Neither the key nor the value can be null.
* 在表中映射特定的key和value,key和value都不可为空
* <p>The value can be retrieved by calling the {@code get} method
* with a key that is equal to the original key.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key or value is null
*/
public V put(K key, V value) {
return putVal(key, value, false);
}
获取元素:
/**
* Returns the value to which the specified key is mapped,
* or {@code null} if this map contains no mapping for the key.
* 返回指定的key对应的value值,如果匹配不到则返回null
* <p>More formally, if this map contains a mapping from a key
* {@code k} to a value {@code v} such that {@code key.equals(k)},
* then this method returns {@code v}; otherwise it returns
* {@code null}. (There can be at most one such mapping.)
*
* @throws NullPointerException if the specified key is null
*/
public V get(Object key) {
Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
int h = spread(key.hashCode());
if ((tab = table) != null && (n = tab.length) > 0 &&
(e = tabAt(tab, (n - 1) & h)) != null) {
if ((eh = e.hash) == h) {
if ((ek = e.key) == key || (ek != null && key.equals(ek)))
return e.val;
}
else if (eh < 0)
return (p = e.find(h, key)) != null ? p.val : null;
while ((e = e.next) != null) {
if (e.hash == h &&
((ek = e.key) == key || (ek != null && key.equals(ek))))
return e.val;
}
}
return null;
}
删除元素:
/**
* Removes the key (and its corresponding value) from this map.
* This method does nothing if the key is not in the map.
* 从集合中删除元素,如果这个key不在集合内,也不会有什么影响。
* @param key the key that needs to be removed
* @return the previous value associated with {@code key}, or
* {@code null} if there was no mapping for {@code key}
* @throws NullPointerException if the specified key is null
*/
public V remove(Object key) {
return replaceNode(key, null, null);
}
总结
本文只是对Java的集合体系做一个大致的介绍,后续章节会针对常用和面试高频的一些集合进行详细的分析。
欢迎点赞收藏关注🤭