ArrayList
注意事项
permits all elements, including null , ArrayList 可以加入null(空值),并且可以是多个。- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)。在多线程情况下,不建议使用ArrayList。
底层操作机制源码分析
- ArrayList中维护了一个Object类型的数组elementData
1
| transient Object[] elementData;
|
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为
10,如需要再次扩容,则扩容elementData为1.5倍。 - 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
无参构造器
1 2 3 4
| private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
|
add方法
1 2 3 4 5
| public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; }
|
ensureCapacityInternal —> calculateCapacity
1 2 3 4 5 6 7
| private static final int DEFAULT_CAPACITY = 10;
private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }
|
有参构造器
1 2 3 4 5 6 7 8 9
| public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}
|
扩容流程
add
1 2 3 4 5
| public boolean add(E e) { ensureCapacityInternal(size + 1); elementData[size++] = e; return true; }
|
ensureCapacityInternal
calculateCapacity界定所需数组大小
1 2 3
| private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
|
ensureExplicitCapacity
1 2 3 4 5 6 7
| private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); }
|
grow
最终调用Arrays.copyOf方法对原数组扩容
1 2 3 4 5 6 7 8 9 10 11 12
| private void grow(int minCapacity) { int oldCapacity = elementData.length; 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); }
|
并发替代方案
- Vector(两倍扩容)
- Vector 用的不多,因为每次对添加的元素上锁,而且使用的是重量级锁synchronized是十分占用资源的,效率低下
- Collections
1
| List<String> list = Collections.synchronizedList(new ArrayList<>());
|
- CopyOnWriteArrayList
- 写时复制技术
- 读的时候并发(多个线程操作)
- 写的时候独立,先复制相同的空间到某个区域,将其写到新区域,旧新合并,并且读新区域(每次加新内容都写到新区域,覆盖合并之前旧区域,读取新区域添加的内容)
1
| List<String> list = new CopyOnWriteArrayList<>();
|
HashMap
注意事项
- Map接口的常用实现类:HashMap、Hashtable和Properties。
- HashMap是 Map 接口使用频率最高的实现类。
- HashMap 是以key-val对的方式来存储数据(HashMap$Node类型)
- key不能重复,但是值可以重复,允许使用null键和null值。
- 如果添加相同的key,则会覆盖原来的key-val ,等同于修改.(key不会替换,val会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.
(jdk8的hashMap底层数组+链表+红黑树) - HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
底层机制及源码分析
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(Ioadfactor)初始化为
0.75 - 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key和准备加入的key相是否等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容。
- 第1次添加,则需要扩容table容量为16,临界值(threshold)为12
(16*0.75) - 以后再扩容,则需要扩容table容量为原来的2倍(32),临界值为原来的2倍,即24,依次类推
- 在Java8中,如果一条链表的元素个数超过 TREEIFY_THRESHOLD(默认是8),并且table的大小>= MIN TREEIFY CAPACITY(默认64),就会进行树化
(红黑树)

关于上图
- (K,V)是一个node,实现了Map.Entry<K,V>
- jdk7的HashMap底层实现
[数组+链表];jdk8底层实现[数组+链表+红黑树]
无参构造器
1 2 3 4
| static final float DEFAULT_LOAD_FACTOR = 0.75f; public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; }
|
put方法
1 2 3
| public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
|
putVal方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { Node<K,V>[] tab; Node<K,V> p; int n, i; 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<K,V> e; K k; if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; else if (p instanceof TreeNode) e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); else { for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } if (e != null) { V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; if (++size > threshold) resize(); afterNodeInsertion(evict); return null; }
|
treeifyBin方法
1 2 3 4 5 6 7 8
|
final void treeifyBin(Node<K,V>[] tab, int hash) { int n, index; Node<K,V> e; if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY) resize(); }
|
并发替代方
ConcurrentHashMap
1
| ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
|