单列集合 (Collection)
单列集合类的根接口,用于:存储一系列符合某种规则的元素。
List 集合
有序,可重复
一、ArrayList
底层数组实现
特点:有序,可重复、非线程安全、查询快,删除慢(需要移动元素)
-
ArrayList 类中的全局变量
private static final int DEFAULT_CAPACITY = 10; //默认初始容量大小10 transient Object[] elementData; //底层数组实现 private int size; //实际元素个数
-
ArrayList 扩容机制【默认10,每次扩容是原来的1.5倍】
private void grow(int minCapacity) { //获取原数组大小 int oldCapacity = elementData.length; //新数组大小 = 原数组大小 + 原数组大小右移一位(右1移相当于除以2) int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 将原数组的值拷贝到新数组,并将新数组赋值给变量 elementData = Arrays.copyOf(elementData, newCapacity); }
-
ArrayList 新增方法
-
未指定位置,追加到后面
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
- 指定位置插入
public void add(int index, E element) { rangeCheckForAdd(index); ensureCapacityInternal(size + 1); //如果在中间插入,元素后移,空出位置 System.arraycopy(elementData, index, elementData, index + 1, size - index); elementData[index] = element; size++; }
-
二、LinkedList
底层双向链表实现 ,
特点:有序,可重复,非线程安全、适用于增删操作
-
LinkedList 类中的全局变量
transient int size = 0; //初始大小0 transient Node
first; //头节点 transient Node last; //尾节点
-
Node为LinkedList的内部类,由此我们可知道 LinkedList 为双向连接结构
private static class Node
{ E item; Node next; Node prev; } Node(Node prev, E element, Node next) { this.item = element; this.next = next; this.prev = prev; }
-
LinkedList 新增元素
public boolean add(E e) { linkLast(e); return true; } void linkLast(E e) { final Node
l = last; final Node newNode = new Node<>(l, e, null); last = newNode; //更换尾节点 if (l == null) first = newNode; else l.next = newNode; size++; modCount++; }
-
LinkedList 删除元素
public boolean remove(Object o) { //遍历删除 if (o == null) { for (Node
x = first; x != null; x = x.next) { if (x.item == null) { unlink(x); return true; } } } else { for (Node x = first; x != null; x = x.next) { if (o.equals(x.item)) { unlink(x); return true; } } } return false; } //删除元素 E unlink(Node x) { // assert x != null; final E element = x.item; final Node next = x.next; final Node prev = x.prev; if (prev == null) { //如果前面没有节点,就把下一个节点赋值给头节点 first = next; } else {//如果前面存在节点,则把前节点指向我的下一节点,置空我的前节点 prev.next = next; x.prev = null; } if (next == null) { last = prev; } else { next.prev = prev; x.next = null; } x.item = null; size--; modCount++; return element; }
三、Vector
查看源代码,会发现每个方法是使用 Synchronized 关键字修饰
Vector与ArrayList,一样底层数组实现
特点:有序,可重复、线程安全
-
Vector 默认初始大小 10
public Vector() { this(10); }
-
Vector 新增方法 (synchronized 保证线程安全)
public synchronized boolean add(E e) { modCount++; ensureCapacityHelper(elementCount + 1); elementData[elementCount++] = e; return true; }
-
Vector 扩容机制 【2倍扩容】
private void grow(int minCapacity) { // 获取当前数组大小 int oldCapacity = elementData.length; // capacityIncrement 默认是 0 ,所以新数组长度 = oldCapacity + oldCapacity int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); } //capacityIncrement 默认是 0 public Vector(int initialCapacity) { this(initialCapacity, 0); } public Vector(int initialCapacity, int capacityIncrement) { super(); if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); this.elementData = new Object[initialCapacity]; this.capacityIncrement = capacityIncrement; }
Set无序集合
无序,不重复
一、HashSet
基于 HashMap 实现的,底层采用 HashMap 来保存元素
特点:无序、不重复,非线程安全。
-
HashSet 类中的全局变量
private transient HashMap
map; //存储数据 private static final Object PRESENT = new Object(); //与后台映射中的对象关联的虚拟值 -
构造函数 【初始化一个 HashMap 存储数据】
public HashSet() { map = new HashMap<>(); }
-
HashSet 新增元素 【可以看出,使用的 Map 存储的】
public boolean add(E e) { return map.put(e, PRESENT)==null; }
-
HashSet 删除元素
public boolean remove(Object o) { return map.remove(o)==PRESENT; }
-
HashSet 只能通过迭代器取出元素,因为没有提供 get 方法。
HashSet set = new HashSet(); Iterator iterator = set.iterator(); while (iterator.hasNext()) { Object next = iterator.next(); System.out.println(next); }
二、TreeSet
特点:有序,不重复
-
TreeSet 类中的全局变量
private transient NavigableMap
m; //TreeMap 的父类是 NavigableMap private static final Object PRESENT = new Object(); -
TreeSet 构造函数,可见底层使用 TreeMap 存储数据。【TreeMap的实现是红黑树算法的实现】
public TreeSet() { this(new TreeMap
()); }
双列集合 (Map)
双列集合类的根接口,用于:存储具有键(Key)、值(Value)映射关系的元素。
一、HashMap
底层:数组 + 链表 + 红黑树
特点:可序列化,线程不安全,key、value可以为空
-
HashMap 全局变量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; //16 默认的初始容量-必须是2的幂 static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量 10 7374 1824 static final float DEFAULT_LOAD_FACTOR = 0.75f; //默认的加载因子 static final int TREEIFY_THRESHOLD = 8; //由链表转化为树的阈值 static final int UNTREEIFY_THRESHOLD = 6; //由树转换成链表的阈值 //最小树形化阈值 ,即 当哈希表中的容量 > 该值时,才允许树形化链表 (即 将链表 转换成红黑树) static final int MIN_TREEIFY_CAPACITY = 64; //---------- Fields ---------- transient Node
[] table; //存储元素的数组 transient Set > entrySet; transient int size; //元素的个数 transient int modCount; // 每次扩容和更改map结构的计数器 int threshold; //临界值 当实际大小(容量*负载因子)超过临界值时,会进行扩容 final float loadFactor; //加载因子 -
HashMap 构造函数
/** * Constructs an empty HashMap with the default initial capacity * (16) and the default load factor (0.75). */ public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted } /** * Constructs an empty HashMap with the specified initial * capacity and the default load factor (0.75). * * @param initialCapacity the initial capacity. * @throws IllegalArgumentException if the initial capacity is negative. */ public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } /** * Constructs an empty HashMap with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); }
-
HashMap 计算扩容的阈值
/** * Returns a power of two size for the given target capacity. * 返回给定目标容量的2次幂大小。 */ static final int tableSizeFor(int cap) { int n = cap - 1; n |= n >>> 1; n |= n >>> 2; n |= n >>> 4; n |= n >>> 8; n |= n >>> 16; return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1; }
-
HashMap 计算 hash 值
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
-
HashMap 新增元素
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); } /** * onlyIfAbsent if true, don't change existing value * evict if false, the table is in creation mode. */ final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) { Node
[] tab; Node p; int n, i; //首次进入 table为 null 初始化数组 if ((tab = table) == null || (n = tab.length) == 0) n = (tab = resize()).length; //计算位置,该位置为空 直接赋值 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); //不为空的情况 else { Node e; K k; //判断 key 值是否相等 if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k)))) e = p; //判断当前是否为红黑树 else if (p instanceof TreeNode) e = ((TreeNode )p).putTreeVal(this, tab, hash, key, value); //当 key 值不同、不属于树结构,作为链表节点插入 else { for (int binCount = 0; ; ++binCount) { //当遍历的节点,next 值为空,插入到此位置 if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); //如果链表长度达到阈值,转化为红黑树 if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } //当 key 值存在相同的,结束循环,不进行插入 if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } //当 e != null 说明链表中存在相同的 key if (e != null) { V oldValue = e.value; // onlyIfAbsent 默认 false ,覆盖原来 value 的值 if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } //增加修改次数 ++modCount; //如果 ++size 大于临界值,进行扩容 if (++size > threshold) resize(); afterNodeInsertion(evict); return null; } -
HashMap 扩容代码【二倍扩容】
/** * Initializes or doubles table size. If null, allocates in * accord with initial capacity target held in field threshold. * Otherwise, because we are using power-of-two expansion, the * elements from each bin must either stay at same index, or move * with a power of two offset in the new table. * 如果为空,则按照字段阈值中持有的初始容量目标进行分配 * 否则,我们使用2的幂展开 * @return the table */ final Node
[] resize() { Node [] oldTab = table; int oldCap = (oldTab == null) ? 0 : oldTab.length; int oldThr = threshold; int newCap, newThr = 0; //当前数组不为空的情况下 if (oldCap > 0) { //如果查过最大容量 1<<30 ,则当前的临界值设为最大整型值 if (oldCap >= MAXIMUM_CAPACITY) { threshold = Integer.MAX_VALUE; return oldTab; } //如果原来长度左移1位(相当于乘2)小于最大容量 并且 大于默认大小 16 的情况下,双倍扩容 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY && oldCap >= DEFAULT_INITIAL_CAPACITY) newThr = oldThr << 1; // double threshold } //使用带有初始容量的构造器时,table容量为初始化得到的threshold else if (oldThr > 0) newCap = oldThr; //默认构造器下进行扩容 else { newCap = DEFAULT_INITIAL_CAPACITY; newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY); } //使用带有初始容量的构造器在此处进行扩容 if (newThr == 0) { float ft = (float)newCap * loadFactor; newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ? (int)ft : Integer.MAX_VALUE); } threshold = newThr; @SuppressWarnings({"rawtypes","unchecked"}) Node [] newTab = (Node [])new Node[newCap]; table = newTab; if (oldTab != null) { for (int j = 0; j < oldCap; ++j) { Node e; if ((e = oldTab[j]) != null) { oldTab[j] = null; //元素后面没有值的情况,计算在新数组的位置进行赋值 if (e.next == null) newTab[e.hash & (newCap - 1)] = e; //元素后面是红黑树的情况下,移动元素 else if (e instanceof TreeNode) ((TreeNode )e).split(this, newTab, j, oldCap); //其他情况,元素后面是链表的情况下,移动元素 else { // preserve order Node loHead = null, loTail = null; Node hiHead = null, hiTail = null; Node next; do { next = e.next; if ((e.hash & oldCap) == 0) { if (loTail == null) loHead = e; else loTail.next = e; loTail = e; } else { if (hiTail == null) hiHead = e; else hiTail.next = e; hiTail = e; } } while ((e = next) != null); if (loTail != null) { loTail.next = null; newTab[j] = loHead; } if (hiTail != null) { hiTail.next = null; newTab[j + oldCap] = hiHead; } } } } } return newTab; }
-
HashMap 获取元素
public V get(Object key) { Node
e; return (e = getNode(hash(key), key)) == null ? null : e.value; } //获取元素 final Node getNode(int hash, Object key) { Node [] tab; Node first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { //key 是否等于 头节点 key if (first.hash == hash && ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { //从红黑树中查找 if (first instanceof TreeNode) return ((TreeNode )first).getTreeNode(hash, key); //从链表查找 do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
二、Hashtable
底层:数组 + 链表
特点:不允许使用 null 作为 key 或 value 。线程安全
-
Hashtable 全局变量
private transient Entry,?>[] table; //存放数据 private transient int count; //元素个数 private int threshold; //当表的大小超过这个阈值时,扩容 private float loadFactor; //加载因子 private transient int modCount = 0; //修改次数 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //数组最大大小
-
Hashtable 构造函数
/** * Constructs a new, empty hashtable with a default initial capacity (11) and load factor (0.75). * 无参构造,默认数组大小11,加载因子 0.75 */ public Hashtable() { this(11, 0.75f); } /** * 指定了大小,默认加载因子 0.75 */ public Hashtable(int initialCapacity) { this(initialCapacity, 0.75f); } /** * Constructs a new, empty hashtable with the specified initial * capacity and the specified load factor. * * @param initialCapacity the initial capacity of the hashtable. * @param loadFactor the load factor of the hashtable. * @exception IllegalArgumentException if the initial capacity is less * than zero, or if the load factor is nonpositive. * 指定大小、加载因子 */ public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; table = new Entry,?>[initialCapacity]; //计算扩容的阈值 数组大小 * 加载因子 threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); }
-
Hashtable 新增元素
public synchronized V put(K key, V value) { // value 为空的情况下,抛出异常 if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry,?> tab[] = table; //当 key 为空的情况下,会抛出空指针异常 int hash = key.hashCode(); //计算该元素应放置的位置 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry
entry = (Entry )tab[index]; //如果该位置存在元素 for(; entry != null ; entry = entry.next) { //如果 key 值相同,将新值赋值给元素 if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } addEntry(hash, key, value, index); return null; } private void addEntry(int hash, K key, V value, int index) { modCount++; Entry,?> tab[] = table; //判断当前大小是否超过阈值,进行扩容 if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entry e = (Entry ) tab[index]; //将新结点插到链表首部 tab[index] = new Entry<>(hash, key, value, e); count++; } -
Hashtable扩容 【二倍加1】
protected void rehash() { int oldCapacity = table.length; Entry,?>[] oldMap = table; // 原大小左移一位 加 1 int newCapacity = (oldCapacity << 1) + 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entry,?>[] newMap = new Entry,?>[newCapacity]; modCount++; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { for (Entry
old = (Entry )oldMap[i] ; old != null ; ) { Entry e = old; old = old.next; int index = (e.hash & 0x7FFFFFFF) % newCapacity; e.next = (Entry )newMap[index]; newMap[index] = e; } } } -
Hashtable 获取元素
public synchronized V get(Object key) { Entry,?> tab[] = table; int hash = key.hashCode(); //计算出位置 int index = (hash & 0x7FFFFFFF) % tab.length; //遍历链表,查询需要的值 for (Entry,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
Collections类
怎么实现线程安全?
//List集合实现线程安全
List l1 = new ArrayList();
List l2 = Collections.synchronizedList(l1);
//Set集合实现线程安全
Set s1 = new HashSet();
Set s2 = Collections.synchronizedSet(s1);
//Map 实现线程安全
Map m1 = new HashMap();
Map m2 = Collections.synchronizedMap(m1);