UDN-企业互联网技术人气社区

板块导航

浏览  : 1009
回复  : 0

[技术交流] java与前端开发:Java容器---实现

[复制链接]
白青青的头像 楼主
发表于 2016-6-22 15:25:06 | 显示全部楼层 |阅读模式
  “工欲善其事,必先利其器”。Java 容器就是我们开发中的利器。

  然而,之前在开发中使用仅仅是容器的一小部分。这次从源码的角度进行深入的理解,一点总结分享给大家。

  这里只列举了非阻塞式的容器;阻塞式的容器,会在后面的并发篇补。

  如果有什么理解不对的地方,欢迎大家在评论中指正~

  ArrayList

  实现: 数组实现

  线程安全: 非线性安全,fail-fast 机制保护

  容量: 初始容量为10;随后每次增加都会变成之前的1.5倍(批量添加除外)。源码如下:
  1. private void grow(int minCapacity) {
  2.     int oldCapacity = elementData.length;
  3.     // 这里是扩容的关键
  4.     int newCapacity = oldCapacity + (oldCapacity >> 1);
  5.     // 批量添加,也有可能会直接等于相加之后的容量
  6.     if(newCapacity - minCapacity < 0) newCapacity = minCapacity;
  7.     if(newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
  8.     elementData = Arrays.copyOf(elementData, newCapacity);
  9. }
复制代码

  效率:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
contains(Object o)
O(n)
O(1)
O(n)
indexOf(Object o)
O(n)
O(1)
O(n)
lastIndexOf(Object o)
O(n)
O(1)
O(n)
toArray()
O(n)
O(n)
O(n)
get(int index)
O(1)
O(1)
O(1)
set(int index, E e)
O(1)
O(1)
O(1)
add(E e)
O(1)
O(1)
O(1)
add(int index, E e)
O(n)
O(1)
O(n)
remove(int index)
O(n)
O(1)
O(n)
remove(Object o)
O(n)
O(1)
O(n)
clear()
O(n)
O(n)
O(n)
addAll(Collection c)
O(m)
O(m)
O(m)
removeAll(Collection c)
O(m*n)
O(m)
O(m*n)
retainAll(Collection c)
O(m*n)
O(m)
O(m*n)

  除了模型中的操作外还有一些特殊的操作:
  1. // 缩小容器的容量到元素的数量
  2. public void trimToSize();
  3. // 确保容量能覆盖 minCapacity 个元素
  4. public synchronized void ensureCapacity(int minCapacity) ;
复制代码

  Vector

  实现: 数组

  线程安全: 是;fast-fail 保护

  容量: 初始容量为10;根据指定的扩容数量或者*2(批量添加除外);核心代码:
  1. private void grow(int minCapacity) {
  2.     int oldCapacity = elementData.length;
  3.     // capacityIncrement 是构造函数中指定的值, 默认为0
  4.     int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
  5.     if (newCapacity - minCapacity < 0) newCapacity = minCapacity;
  6.     if (newCapacity - MAX_ARRAY_SIZE > 0)  newCapacity = hugeCapacity(minCapacity);
  7.     elementData = Arrays.copyOf(elementData, newCapacity);
  8. }
复制代码

  效率:

  效率与ArrayList相似,但是每一个操作都需要获取锁,时间开销要比ArrayList大。

操作
平均
最好
最差
add(E e)
O(1)
O(1)
O(1)
add(int index, E e)
O(n)
O(1)
O(n)
get(int index)
O(1)
O(1)
O(1)
remove(int index)
O(n)
O(1)
O(n)
remove(Object o)
O(n)
O(1)
O(n)
removeAll(Collection c)
O(n)
O(n)
O(n)
retainAll(Collection)
O(n)
O(n)
O(n)
indexOf(Object)
O(n)
O(1)
O(n)

  Stack

  实现: Vector

  线程安全: 是;fast-fail保护

  容量: 同Vector

  效率:

操作
平均
最好
最差
push(E e)
O(1)
O(1)
O(1)
pop()
O(1)
O(1)
O(1)
peek()
O(1)
O(1)
O(1)
empty()
O(1)
O(1)
O(1)

  LinkedList

  实现: 双指针链表

  线程安全:否;fast-fail保护

  容量: 有size();无容量

  效率:

  作为 List 来说:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
contains
O(n)
O(1)
O(n)
add(E e)
O(1)
O(1)
O(1)
remove(Object o)
O(n)
O(1)
O(n)
containsAll(Collection c)
O(m*n)
O(m)
O(m*n)
toArray()
O(n)
O(n)
O(n)
removeAll(Collection c)
O(m*n)
O(m)
O(m*n)
retainAll(Collection c)
O(m*n)
O(m)
O(m*n)
removeAll(Collection c)
O(m*n)
O(m)
O(m*n)
retainAll(Collection c)
O(m*n)
O(m)
O(m*n)
clear()
O(1)
O(1)
O(1)
get(int index)
O(n)
O(1)
O(n)
set(int index,E e)
O(n)
O(1)
O(n)
add(int index, E e)
O(n)
O(1)
O(n)
remove(int index)
O(n)
O(1)
O(n)
indexOf(Object o)
O(n)
O(1)
O(n)
lastIndexOf(Object o)
O(n)
O(1)
O(n)


  作为 Queue 来说:

操作
平均
最好
最差
add(E e)
O(1)
O(1)
O(1)
offer(E e)
O(1)
O(1)
O(1)
remove()
O(1)
O(1)
O(1)
poll()
O(1)
O(1)
O(1)
element()
O(1)
O(1)
O(1)
peek()
O(1)
O(1)
O(1)


  作为 Deque 来说:

操作
平均
最好
最差
xxxFisrt
O(1)
O(1)
O(1)
xxxLast
O(1)
O(1)
O(1)
removeFirstOccurrence(Object o)
O(n)
O(1)
O(n)
removeLastOccurrence(Object o)
O(n)
O(1)
O(n)
pop
O(1)
O(1)
O(1)
push
O(1)
O(1)
O(1)

  PriorityQueue

  特征: 容器中元素的 出顺序 不是元素的 插入顺序,而是一个大小顺序中的最值。

  实现方式: 小顶堆

  线程安全: 否;fail-fast保护

  容量: 默认初始容量11;容量小的时候*2,容量大的时候+50%。核心代码如下:
  1. private void grow(int minCapacity) {
  2. int oldCapacity = queue.length;
  3. // 小于64的时候增倍;大于64的时候增加50%
  4. int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1));
  5. if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity);
  6. // queue是存放元素的数组
  7. queue = Arrays.copyOf(queue, newCapacity);
复制代码

  效率:

  如果只是用到确定数量个元素的情况下,它是比列表排序的效率要高的。随机数列的排序平均都要 O(n*log(n)) 了,而它最高的复杂度仅为O(log(n))

  但是,如果所有元素都要遍历一次取出来,那效率与就跟堆排序是一样的O(n*log(n)) 。

操作
平均
最好
最差
add(E e)
O(log(n))
O(1)
O(log(n))
offer(E e)
O(log(n))
O(1)
O(log(n))
remove()
O(log(n))
O(log(n))
O(log(n))
poll()
O(log(n))
O(log(n))
O(log(n))
peek()
O(1)
O(1)
O(1)
element()
O(1)
O(1)
O(1)
toArray()
O(n)
O(n)
O(n)
clear()
O(n)
O(n)
O(n)

  HashSet

  特征: 高效率的集合容器

  实现: HashMap

  线程安全: 否;failfast保护

  容量: 同HashMap

  效率:同HashMap

  LinkedHashSet

  特征: 高效率的集合容器,能保存插入顺序

  实现: LinkedHashMap

  线程安全:否; failfast保护

  容量: 同 LinkedHashMap

  效率: 同 LinkedHashMap

  TreeSet

  特征: 带全排序的集合容器

  实现:TreeMap

  线程安全:否;failfast保护

  容量:无

  效率:同 TreeMap

  HashMap

  特征:高效率的映射表

  实现: 开散列

  线程安全:否;fail-fast保护

  是否支持null: 是

  容量: 默认初始容量16;容量总是2的幂;默认加载因子0.75;扩容后的容量由当前容量和加载因子决定,这里的扩容代码不是很直接,需要联系几个方法来看:
  1. // 添加一组映射的时候
  2. void addEntry(int hash, K key, V value, int bucketIndex) {
  3.     // 容量超过threshold并且对应的hash值存在在冲突的时候,开始扩容
  4.     if ((size >= threshold) && (null != table[bucketIndex])) {
  5.         // 扩展为之前的2倍
  6.         resize(2 * table.length);
  7.         hash = (null != key) ? hash(key) : 0;
  8.         bucketIndex = indexFor(hash, table.length);
  9.     }
  10.     createEntry(hash, key, value, bucketIndex);
  11. }

  12. // 这个方法的作用在于将数组扩展到相应的容量,
  13. // 并把原数组中的元素全部重新计算位置,转移到新数组中
  14. // 重新计算下一个threshold
  15. void resize(int newCapacity) {
  16.     Entry[] oldTable = table;
  17.     int oldCapacity = oldTable.length;
  18.     if (oldCapacity == MAXIMUM_CAPACITY) {
  19.         threshold = Integer.MAX_VALUE;
  20.         return;
  21.     }
  22.     Entry[] newTable = new Entry[newCapacity];
  23.     transfer(newTable, initHashSeedAsNeeded(newCapacity));
  24.     table = newTable;
  25.     threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
  26. }

  27. // 这个方法在容器第一次添加元素的时候调用,使用toSize做参数的目的是避免第一次添加元素的时候多次扩容
  28. private void inflateTable(int toSize) {
  29.     int capacity = roundUpToPowerOf2(toSize);
  30.     threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
  31.     table = new Entry[capacity];
  32.     initHashSeedAsNeeded(capacity);
  33. }
复制代码

  效率:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
get
O(1)
O(1)
O(n)
getEntry(Object key)
O(1)
O(1)
O(n)
put(K key, V value)
O(1)
O(1)
O(n)
remove
O(1)
O(1)
O(n)
clear
O(n)
O(n)
O(n)
containsValue
O(n)
O(1)
O(n)
putAll(Collection c)
O(m)
O(m)
O(m*n)

  LinkedHashMap

  特征: 在HashMap的基础上添加了,特定规则的顺序保存

  实现:开散列 + 双指针链表环

  线程安全: 否;fast-fail保护

  是否支持null: 是

  容量: 同HashMap

  特征:它在HashMap的基础上,对Entry的封装中加了两个指针记录顺序。也就是说,这里既有散列保证随机访问效率,又有链表记录顺序。

  这里的顺序不是指大小顺序。而是插入顺序,或者访问次数的顺序。所以它可以用来做LRU缓存容器,不过在此之前需要对他做一些封装,需要定义缓存的体积和重写一些方法:
  1. //这里的accessOrder要设置为true
  2. public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
  3. }
  4. // 缓存容量
  5. private static final int MAX_ENTRIES = 100;
  6. // 设置为超容量即删除
  7. protected boolean removeEldestEntry(Map.Entry eldest) {
  8.     return size() > MAX_ENTRIES;
  9. }
复制代码

  TreeMap

  特征: 全排序的映射表

  实现:红黑树

  线程安全: 否,fastfail保护

  是否支持null:是

  容量:无

  特征:访问的时候根据二叉查找树来查找,插入,删除的时候需要根据红黑树的规则来平衡树。

  效率:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
containsKey(Object key)
O(log(n))
O(1)
O(log(n))
containsValue(Object value)
O(n)
O(1)
O(n)
get(Object key)
O(log(n))
O(log(n))
O(log(n))
put(K key, V value)
O(log(n))
O(log(n))
O(log(n))
remove(Object key)
O(log(n))
O(log(n))
O(log(n))
clear()
O(1)
O(1)
O(1)
firstKey()
O(log(n))
O(log(n))
O(log(n))
lastKey()
O(log(n))
O(log(n))
O(log(n))
putAll(Map map)
O(log(n))
O(log(n))
O(log(n))
firstEntry()
O(log(n))
O(log(n))
O(log(n))
lastEntry()
O(log(n))
O(log(n))
O(log(n))
pollFirstEntry()
O(log(n))
O(log(n))
O(log(n))
pollLastEntry()
O(log(n))
O(log(n))
O(log(n))
lowerXXX(K key)
O(log(n))
O(log(n))
O(log(n))
floorXXX(K key)
O(log(n))
O(log(n))
O(log(n))
ceilingXXX(K key)
O(log(n))
O(log(n))
O(log(n))
higherXXX(K key)
O(log(n))
O(log(n))
O(log(n))

  Hashtable

  特征:线程安全的高效映射表

  实现: 开散列

  线程安全: 是;fail-fast保护

  是否支持null: 否

  容量:默认初始容量11;加载因子0.75;由加载因子决定扩容时机,默认size超过容量的0.75需要扩容。核心代码如下:
  1. public synchronized V put(K key, V value) {
  2.     // ...
  3.     // 容器中元素数量超过threshold时,扩容并重新计算hash,然后再添加元素
  4.     if (count >= threshold) {
  5.         // Rehash the table if the threshold is exceeded
  6.         rehash();
  7.         tab = table;
  8.         hash = hash(key);
  9.         index = (hash & 0x7FFFFFFF) % tab.length;
  10.     }
  11.     // ...
  12. }

  13. protected void rehash() {
  14.     int oldCapacity = table.length;
  15.     Entry<K,V>[] oldMap = table;
  16.     // 新容量是旧容量的2倍
  17.     int newCapacity = (oldCapacity << 1) + 1;
  18.     if (newCapacity - MAX_ARRAY_SIZE > 0) {
  19.         if (oldCapacity == MAX_ARRAY_SIZE)
  20.             // Keep running with MAX_ARRAY_SIZE buckets
  21.             return;
  22.         newCapacity = MAX_ARRAY_SIZE;
  23.     }
  24.     Entry<K,V>[] newMap = new Entry[newCapacity];
  25.     modCount++;
  26.     // 这里计算新的阈值
  27.     threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
  28.     boolean rehash = initHashSeedAsNeeded(newCapacity);
  29.     table = newMap;
  30.     // 然后重新计算hash对应的位置
  31.     for (int i = oldCapacity ; i-- > 0 ;) {
  32.         for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
  33.             Entry<K,V> e = old;
  34.             old = old.next;
  35.             if (rehash) {
  36.                 e.hash = hash(e.key);
  37.             }
  38.             int index = (e.hash & 0x7FFFFFFF) % newCapacity;
  39.             e.next = newMap[index];
  40.             newMap[index] = e;
  41.         }
  42.     }
  43. }
复制代码

  效率:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
get
O(1)
O(1)
O(n)
getEntry(Object key)
O(1)
O(1)
O(n)
put(K key, V value)
O(1)
O(1)
O(n)
remove
O(1)
O(1)
O(n)
clear
O(n)
O(n)
O(n)
containsValue
O(n)
O(1)
O(n)
putAll(Collection c)
O(m)
O(m)
O(m*n)

  WeakHashMap

  特征:它的Map.Entry类设计的比较特殊继承了弱引用类。key是作为弱引用持有的对象。这意味着容器中的对象在没有外部引用持有的时候随时都有可能被GC回收。所以它可以被用来做缓存。
  1.     private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
  2.         V value;
  3.         int hash;
  4.         Entry<K,V> next;

  5.         /**
  6.          * Creates new entry.
  7.          */
  8.         Entry(Object key, V value,
  9.               ReferenceQueue<Object> queue,
  10.               int hash, Entry<K,V> next) {
  11.             // key是弱引用持有的对象
  12.             super(key, queue);
  13.             this.value = value;
  14.             this.hash  = hash;
  15.             this.next  = next;
  16.         }
  17.     }
复制代码

  实现: 开散列 + WeakReference + ReferenceQueue

  线程安全: 否; fast-fail保护

  是否支持null:是

  容量:默认初始容量16;默认加载因子0.75;容量总是2的幂,每次扩容都*2;如果在扩容过程中发现大量的元素被回收,可能会终止扩容,继续使用之前的容量。核心代码如下:
  1. public V put(K key, V value) {
  2.     // ...
  3.     tab[i] = new Entry<>(k, value, queue, h, e);
  4.     if (++size >= threshold)
  5.         resize(tab.length * 2);
  6. }

  7. void resize(int newCapacity) {
  8.     Entry<K,V>[] oldTable = getTable();
  9.     int oldCapacity = oldTable.length;
  10.     if (oldCapacity == MAXIMUM_CAPACITY) {
  11.         threshold = Integer.MAX_VALUE;
  12.         return;
  13.     }
  14.     Entry<K,V>[] newTable = newTable(newCapacity);
  15.     boolean oldAltHashing = useAltHashing;
  16.     useAltHashing |= sun.misc.VM.isBooted() &&
  17.             (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
  18.     boolean rehash = oldAltHashing ^ useAltHashing;
  19.     transfer(oldTable, newTable, rehash);
  20.     table = newTable;
  21.     /*
  22.      * 如果在扩容过程中删除的元素非常的多,填充数不到阈值的一半的时候
  23.      * 使用回之前容量的数组
  24.      */
  25.     if (size >= threshold / 2) {
  26.         threshold = (int)(newCapacity * loadFactor);
  27.     } else {
  28.         expungeStaleEntries();
  29.         transfer(newTable, oldTable, false);
  30.         table = oldTable;
  31.     }
  32. }

  33. /** Transfers all entries from src to dest tables */
  34. private void transfer(Entry<K,V>[] src, Entry<K,V>[] dest, boolean rehash) {
  35.     for (int j = 0; j < src.length; ++j) {
  36.         Entry<K,V> e = src[j];
  37.         src[j] = null;
  38.         while (e != null) {
  39.             Entry<K,V> next = e.next;
  40.             Object key = e.get();
  41.             // 注意,这里在转移元素的过程中会删除一些已经被垃圾回收的元素
  42.             if (key == null) {
  43.                 e.next = null;  // Help GC
  44.                 e.value = null; //  "   "
  45.                 size--;
  46.             } else {
  47.                 if (rehash) {
  48.                     e.hash = hash(key);
  49.                 }
  50.                 int i = indexFor(e.hash, dest.length);
  51.                 e.next = dest[i];
  52.                 dest[i] = e;
  53.             }
  54.             e = next;
  55.         }
  56.     }
  57. }
复制代码

  效率:

操作
平均
最好
最差
size()
O(n)
O(n)
O(n)
isEmpty()
O(n)
O(n)
O(n)
get
O(1)
O(1)
O(n)
getEntry(Object key)
O(1)
O(1)
O(n)
put(K key, V value)
O(1)
O(1)
O(n)
remove
O(1)
O(1)
O(n)
clear
O(n)
O(n)
O(n)
containsValue
O(n)
O(1)
O(n)
putAll(Collection c)
O(m)
O(m)
O(m*n)

  IdentityHashMap

  特征: 使用“引用相等”而非 equals 相等。只有两个 key 完全是同一个对象的时候才判定为相等。

  实现: 闭散列;没有用 Map.Entry 类,数组偶数位置放 key,奇数位置放 value

  线程安全: 否;fast-fail保护

  是否支持null:是

  容量:默认初始容量32;加载因子2/3;容量一定为2的幂;核心代码:
  1. public V put(K key, V value) {
  2.         // ...
  3.         // 超过阈值,扩容
  4.         if (++size >= threshold)
  5.             resize(len); // len == 2 * current capacity.
  6.         // ...
  7.     }

  8.     private void resize(int newCapacity) {
  9.         // assert (newCapacity & -newCapacity) == newCapacity; // power of 2
  10.         // 原容量*2
  11.         int newLength = newCapacity * 2;

  12.         Object[] oldTable = table;
  13.         int oldLength = oldTable.length;
  14.         // 这里重新计算阈值
  15.         if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further
  16.             if (threshold == MAXIMUM_CAPACITY-1)
  17.                 throw new IllegalStateException("Capacity exhausted.");
  18.             threshold = MAXIMUM_CAPACITY-1;  // Gigantic map!
  19.             return;
  20.         }
  21.         if (oldLength >= newLength)
  22.             return;

  23.         Object[] newTable = new Object[newLength];
  24.         threshold = newLength / 3;
  25.         // 重新计算位置, 由于这是闭散列,需要对所有元素重新计算
  26.         for (int j = 0; j < oldLength; j += 2) {
  27.             Object key = oldTable[j];
  28.             if (key != null) {
  29.                 Object value = oldTable[j+1];
  30.                 oldTable[j] = null;
  31.                 oldTable[j+1] = null;
  32.                 int i = hash(key, newLength);
  33.                 while (newTable[i] != null)
  34.                     i = nextKeyIndex(i, newLength);
  35.                 newTable[i] = key;
  36.                 newTable[i + 1] = value;
  37.             }
  38.         }
  39.         table = newTable;
  40.     }
复制代码

  效率:

操作
平均
最好
最差
size()
O(1)
O(1)
O(1)
isEmpty()
O(1)
O(1)
O(1)
get
O(1)
O(1)
O(n)
getEntry(Object key)
O(1)
O(1)
O(n)
put(K key, V value)
O(1)
O(1)
O(n)
remove
O(1)
O(1)
O(n)
clear
O(n)
O(n)
O(n)
containsKey(Object key)
O(1)
O(1)
O(n)
containsValue(Object value)
O(n)
O(1)
O(n)
putAll(Collection c)
O(m)
O(m)
O(m*n)

相关帖子

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关于我们
联系我们
  • 电话:010-86393388
  • 邮件:udn@yonyou.com
  • 地址:北京市海淀区北清路68号
移动客户端下载
关注我们
  • 微信公众号:yonyouudn
  • 扫描右侧二维码关注我们
  • 专注企业互联网的技术社区
版权所有:用友网络科技股份有限公司82041 京ICP备05007539号-11 京公网网备安1101080209224 Powered by Discuz!
快速回复 返回列表 返回顶部