java数据结构-collection,map (1)
一,数据结构关系图
大致的数据结构关系图
二,List
ArrayList:
1.底层是数组(看证明【底层数组】)
2.ArrayList的Size(逻辑长度)与存放数据的数组长度(物理长度)是2个东西,不是同一个。(【逻辑长度与物理长度】)
3.构造函数new ArrayList(),此时数组size为0,存储数据的数组为空。初始化存储数据的数组是在添加第一个元素的时候。并且数组的长度是10.(看证明【初始数组长度】)
4.扩容是1.5倍扩容(看证明【扩容】)
【底层数组】
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // non-private to simplify nested class access
【逻辑长度与物理长度】
【初始数组长度】
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_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.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
证明步骤:
1.new ArrayList()
2.查看size和elementData.length size -> 0 length -> 0
3.添加第一个元素,重复步骤2 size -> 1 length -> 10
4.添加9个元素,重复步骤2 size -> 10 length -> 10
5.添加第11个元素,重复步骤2 size -> 11 length -> 15
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
// 第1处断点
System.out.println(list.size()); // 0
list.add("xxc");
// 第2处断点
System.out.println(list.size()); // 1
for (int i = 0; i < 9; i++) {
list.add(String.valueOf(i));
}
// 第3处断点
// toArrayTe();
System.out.println(list.size()); // 10
list.add("sd");
// 第4处断点
System.out.println(list.size()); // 11
}
至此证明完成
【扩容】
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// 新的容量 = 旧容量+旧容量>> 1(可以看成旧容量 /2 用int接)
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
HashMap
1.8以后
结论:
底层:数组+链表(红黑树)
利用无参构造创建的HashMap,再put第一个的时候,初始化数组。大小为16
put方法:1.先获取key的hash,并且与底层数组长度【与】运算,获取key的位置下标,然后进行放入。
resize方法:1.基础逻辑,需要重新放值map保存的元素信息。(因为put方法,放置下标的底层数组长度发生变化)
底层:
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
TreeNode<K,V> 继承于 Node<K,V>。
无参构造方法
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
无参构造再插入时初始化数组。
**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 第一次放,肯定是空的table
if ((tab = table) == null || (n = tab.length) == 0)
// 于是会调用到resize()方法。初始化为 16
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
put 的逻辑:
1.获取K的hash
2.判断底层数组是否为空,空则初始化
3.获取K的下标,用K的hash【与】数组下标-1获取。
4.判断下标上是否有值,没,则直接放入
5.下标上有值,则判断是否需要覆盖value
6.是否转换为了树形结构,然后树的操作
7.尾插,放到链表。
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
// 2.判断底层数组是否为空,空则初始化
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
// 3.获取K的下标,用K的hash【与】数组下标-1获取。
// 4.判断下标上是否有值,没,则直接放入
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
// 5.下标上有值,则判断是否需要覆盖value
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
// 6.是否转换为了树形结构,然后树的操作
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
// 7.尾插,放到链表。
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
resize()方法:
扩容,会将所有节点都重新确定在新数组的放值位置。
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
// 用新的扩容后的数组,替换原来的旧数组。并在下面的方法赋值。
table = newTab;
if (oldTab != null) {
for (int j = 0; j < oldCap; ++j) {
Node<K,V> 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<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// 相当牛皮的操作,移位链表上的数据。
// 做个记录,这回还没看明白。。。
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> 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;
}
搬运一个大佬的resize()讲解:
https://blog.csdn.net/weixin_44111477/article/details/103036209