The Collection Collection
List
ArrayList
attribute
// Default initialization length
private static final int DEFAULT_CAPACITY = 10;
/ / an empty array
private static final Object[] EMPTY_ELEMENTDATA = {};
// Default empty array
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// The built array
transient Object[] elementData;
// Array length
private int size;
AbstractArrayList = AbstractArrayList */
protected transient int modCount = 0;
Copy the code
Initialize the
// 1. Create a default empty array object with no arguments
public ArrayList(a) {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
Create an array object of the specified length
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// Create an empty array object with a length of 0
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); }}// 3. The argument is the construction of the collection
public ArrayList(Collection<? extends E> c) {
// Convert the collection to an array object
Object[] a = c.toArray();
if((size = a.length) ! =0) {
// Sets of type ArrayList are directly converted to the current object
if (c.getClass() == ArrayList.class) {
elementData = a;
} else {
// Copies the transformed array and builds the copied values into the current ArrayList objectelementData = Arrays.copyOf(a, size, Object[].class); }}else {
// replace with empty array.elementData = EMPTY_ELEMENTDATA; }}Copy the code
The add method
// Add a single element
public boolean add(E e) {
/** * 1. Check the length of the array and modify the value * 2. New elements are added to the array */
ensureCapacityInternal(size + 1); // Increments modCount!!
// Add the element to the last available space
elementData[size++] = e;
return true;
}
Copy the code
/** * adds the element to the specified index position * 1. Verifies that the subscript meets the current set * 2. */
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
// Moves the specified index index and subsequent elements backward
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
// Assign the index subscript to the array
elementData[index] = element;
size++;
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
Copy the code
Check and expand the array before adding:
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// Return the length of the array
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// Check whether the current array is an empty array object
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// Compare the default length with the current minimum length and return the maximum length
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// Make sure the array is long. If there is no available length, expand the array
private void ensureExplicitCapacity(int minCapacity) {
// The number of changes is increased by 1
modCount++;
// overflow-conscious code
// Compare the required minimum length with the maximum length of the current array to determine whether the array needs to be expanded
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
// The maximum length of the current array
int oldCapacity = elementData.length;
// The maximum length of the new array is 0.5 times larger than the current array
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// If the array length exceeds the maximum length that can be created
// Returns the maximum number of arrays that can be created
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// Copy the data from the old array into the newly created array
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}
Copy the code
The remove method
// Remove the labeled element in the array
public E remove(int index) {
// Check whether the current subscript is out of range of the array
rangeCheck(index);
// The number of times the array has been modified increases by 1
modCount++;
// Retrieves the value to be deleted
E oldValue = elementData(index);
// The subscript of the element to be deleted
int numMoved = size - index - 1;
if (numMoved > 0)
// Move the array element after the specified index+1 position to the front of the image
System.arraycopy(elementData, index+1, elementData, index, numMoved);
// Empty the last element of the array to facilitate JVM recycling
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// Deletes the specified element
public boolean remove(Object o) {
if (o == null) {
// If the element is empty, iterate over the number group and delete the empty element
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true; }}else {
// Iterate through the array to delete the specified element
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true; }}return false;
}
// The logic to remove elements from an array
private void fastRemove(int index) {
// The number of times the array has been modified increases by 1
modCount++;
// The subscript of the element to be deleted
int numMoved = size - index - 1;
if (numMoved > 0)
// Move the array element after the specified index+1 position to the front of the image
System.arraycopy(elementData, index+1, elementData, index,numMoved);
// Empty the last element of the array to facilitate JVM recycling
elementData[--size] = null; // clear to let GC do its work
}
Copy the code
LinkedList
attribute
// List length
transient int size = 0;
/ / the first node
transient Node<E> first;
/ / end nodes
transient Node<E> last;
AbstractArrayList = AbstractArrayList */
protected transient int modCount = 0;
Copy the code
Initialize the
// Create a linked list with no arguments
public LinkedList(a) {}
// A collection object with parameters
public LinkedList(Collection<? extends E> c) {
this(a); addAll(c); }Copy the code
// addAll method to add collection data to the linked list
// Add one by one
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/** * add the collection to the list **@paramIndex Position of the node to be inserted *@paramC Set data to be inserted */
public boolean addAll(int index, Collection<? extends E> c) {
// Check whether the specified node position is in the linked list
checkPositionIndex(index);
// The collection to be inserted is converted to an array object
Object[] a = c.toArray();
// The length of the array to be inserted
int numNew = a.length;
if (numNew == 0)
return false;
// Temporary node, the last node before pre, succ current node
Node<E> pred, succ;
// Insert node position is the tail node
if (index == size) {
succ = null;
pred = last;
} else {
// Insert the node into the middle part of the node
succ = node(index);
pred = succ.prev;
}
// iterate over the insert node
for (Object o : a) {
@SuppressWarnings("unchecked")
E e = (E) o;
// Create a node
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
// A new node is inserted, and the pointer moves backwards
pred = newNode;
}
// Connect the disconnected list to the inserted node
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
// Change the length of the list
size += numNew;
// Modify the number of set changes
modCount++;
return true;
}
Copy the code
The add method
/ / new
public boolean add(E e) {
linkLast(e);
return true;
}
// Use the tail insertion method to add
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
// Insert nodes at the head of the list
public void addFirst(E e) {
linkFirst(e);
}
// Insert a node with a header
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
Copy the code
The remove method
Empty parameter delete: remove()
Data of the first node of the node to be deleted
// Delete node elements
public E remove(a) {
return removeFirst();
}
// Delete the first node element
public E removeFirst(a) {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
Copy the code
Remove the first element: removeFirst()
public E removeFirst(a) {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
Copy the code
The first node breaks the chain
// Disconnect the first node from the list
private E unlinkFirst(Node<E> f) {
// assert f == first && f ! = null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
Copy the code
Remove (Object o)
// Remove the specified element from the list
public boolean remove(Object o) {
// Whether the element to be deleted is empty
if (o == null) {
// Iterate from the first node
for(Node<E> x = first; x ! =null; x = x.next) {
// Check whether the element is to be deleted
if (x.item == null) {
// Delete the link
// For details, see the chain breaking operation
unlink(x);
return true; }}}else {
// The element to be deleted is not empty
for(Node<E> x = first; x ! =null; x = x.next) {
// Check whether the element is to be deleted
if (o.equals(x.item)) {
// Delete the link
// For details, see the chain breaking operation
unlink(x);
return true; }}}return false;
}
Copy the code
Remove (int index)
public E remove(int index) {
// Verify that the specified location is in the list
checkElementIndex(index);
// Delete the link
// For details, see the chain breaking operation
return unlink(node(index));
}
private void checkElementIndex(int index) {
if(! isElementIndex(index))throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
Node<E> node(int index) {
// assert isElementIndex(index);
// Determine whether the current position is in the first half or the second half
// Walk through the list to find elements
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
returnx; }}Copy the code
Break the chain operation
// Break the chain to remove elements
// the x node is not empty
E unlink(Node<E> x) {
// assert x ! = null;
// Current node value
final E element = x.item;
// The next node of the current node
final Node<E> next = x.next;
// The last node of the current node
final Node<E> prev = x.prev;
// If the last node of the current node is empty, the current node is the first node
if (prev == null) {
// Set the next node to the first node
first = next;
} else {
// Set the next pointer to the next node of the current node
// (graphic: wechat image _20210316145013.png)
prev.next = next;
x.prev = null;
}
// If the next node of the current node is empty, the current node is the last node
if (next == null) {
// Set the last node to the tail node
last = prev;
} else {
// Add the current node's
// image :(wechat image _2021031614501.png)
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
Copy the code
Remove endpoints: removeLast()
public E removeLast(a) {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
// The end node breaks the chain
private E unlinkLast(Node<E> l) {
// assert l == last && l ! = null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
Copy the code
RemoveFirstOccurrence (Object O)
public boolean removeFirstOccurrence(Object o) {
// Call the delete method
return remove(o);
}
Copy the code
Map
HashMap
attribute
/ / = = = = = = = = = = = = = = = = = = = = default configuration = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
/* The default initial capacity - MUST be a power of two. */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
/** * maximum length, * The maximum capacity, used if a higher value is implicitly specified * by either of the constructors with arguments. * MUST be a power of two < = 1 < < 30. * /
static final int MAXIMUM_CAPACITY = 1 << 30;
/* The load factor used when none specified in constructor. */
static final float DEFAULT_LOAD_FACTOR = 0.75 f;
* The bin count threshold for using a tree rather than list for a * bin. Bins are converted to trees when adding an element to a * bin with at least this many nodes. The value must be greater * than 2 and should be at least 8 to mesh with assumptions in * tree removal about conversion back to plain bins upon * shrinkage. */
static final int TREEIFY_THRESHOLD = 8;
/** * The bin count threshold for untreeifying a (split) bin during a * resize operation. Should be less than TREEIFY_THRESHOLD, and at * most 6 to mesh with shrinkage detection under removal. */
static final int UNTREEIFY_THRESHOLD = 6;
/** * The minimum size of the array when the list is converted to a red-black tree * Tree operations will not exist * The smallest table capacity for which bins may be treeified. * (Otherwise The table is resized if too many nodes in a bin.) * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts * between resizing and treeification thresholds. */
static final int MIN_TREEIFY_CAPACITY = 64;
/* ---------------- Fields -------------- */
/** * array, initialized at first use, Allocated tokens * 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;
/** * Holds cached entrySet(). Note that AbstractMap fields are used * for keySet() and values(). */
transient Set<Map.Entry<K,V>> entrySet;
/** * The number of key-value mappings contained in this map. */
transient int size;
/** * The number of times this HashMap has been structurally modified * Structural modifications are those that change the number of mappings in * the HashMap or otherwise modify its internal structure (e.g., * rehash). This field is used to make iterators on Collection-views of * the HashMap fail-fast. (See ConcurrentModificationException). */
transient int modCount;
/** * The next size value at which to resize (capacity * load factor). **@serial* /
// (The javadoc description is true upon serialization.
// Additionally, if the table array has not been allocated, this
// field holds the initial array capacity, or zero signifying
// DEFAULT_INITIAL_CAPACITY.)
int threshold;
/** * The load factor for The hash table. **@serial* /
final float loadFactor;
Copy the code
Defined data structures
Node
static class Node<K.V> implements Map.Entry<K.V> {
// Hash value of the node
final int hash;
// The key value in the key-value pair
final K key;
// The value of the key-value pair
V value;
// Pointer to the next node
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
/ /... Contains get, set, hashCode, and toString methods
}
Copy the code
TreeNode
static final class TreeNode<K.V> extends LinkedHashMap.Entry<K.V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
/ /... Methods in nodes
}
// LinkedHashMap.Entry
static class Entry<K.V> extends HashMap.Node<K.V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next); }}Copy the code
Initialize the
// An empty parameter construct that specifies the default load factor
public HashMap(a) {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
// Specify a constructor for the initialization length
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/** * specifies the initialization length and load factor constructor **@paramInitialCapacity Initialization length *@paramLoadFactor loadFactor */
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);
}
// The argument is a collection constructor
// Default load factor
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
/** * Implements Map. PutAll and Map constructor@param m the map
* @paramEvict false when initially constructing this map, else * true (relayed to method afterNodeInsertion). True: uninitialized call */
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
// Set is empty
if (table == null) { // pre-size
// Calculate the required map set length
// The underlying table of HashMap will be expanded when entry_count>table_size*load_factory is used.
// To prevent HashMap from expanding, table_size >= entry_count/load_factor is required.
// Size in the formula ((float)s/loadFactor) + 1.0f is computed using float, + 1.0f is computed because ((float)s/loadFactor) is computed using float and is rounded when converted to an integer
// In order to ensure that the final calculated size is large enough not to trigger expansion, + 1.0f operation is performed
float ft = ((float)s / loadFactor) + 1.0 F;
// The required length is compared to the maximum length
int t = ((ft < (float)MAXIMUM_CAPACITY) ? (int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
// The perturbation function recalculates the required length
threshold = tableSizeFor(t);
}
else if (s > threshold)
// Perform capacity expansion
resize();
// Iterate over the collection to be inserted, saving the element to the new collection
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict); }}}Copy the code
Put method
// Save the element
public V put(K key, V value) {
return putVal(hash(key), key, value, false.true);
}
Copy the code
PutVal method
/ * * * *@paramHash Hash value of the key in the map *@paramKey Indicates the key value * in the key-value pair@paramValue Value * in a key value pair@paramOnlyIfAbsent (True /false) The current value * is not changed when the value is true@paramEvict false: initial call, true: uninitial call */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
// the TAB object, the data node in p, the length of n, the subscript in I
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
// If the array is empty or its length is 0, the array is initialized
n = (tab = resize()).length;
// Check whether the new key has data in the corresponding position
// Whether a hash collision occurs
if ((p = tab[i = (n - 1) & hash]) == null)
// Add the new element to the array
tab[i] = newNode(hash, key, value, null);
else {
// A hash collision occurs
// p is the original data node
Node<K,V> e; K k;
// p.hash == hash The hash value of the original data node is the same as that of the newly stored node
// (k = p.key) == key The key of the original data node is the same as the key address of the newly stored node
// (key ! = null && key.equals(k)) The newly stored node is not null and has the same value as the original node
if(p.hash == hash && ((k = p.key) == key || (key ! =null && key.equals(k))))
// The key of the newly stored node exists in the map
e = p;
else if (p instanceof TreeNode)
// Check whether it is a tree node
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//
for (int binCount = 0; ; ++binCount) {
// Find the free location to save the node
if ((e = p.next) == null) {
// Create a node
p.next = newNode(hash, key, value, null);
// Check whether the list length is 8
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
// Lists are converted to red-black trees
treeifyBin(tab, hash);
break;
}
// If the key value of the node is the same as that of the current node, the loop is broken
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k))))
break;
// Move the pointer backp = e; }}// Existing nodes in map are not empty
if(e ! =null) { // existing mapping for key
V oldValue = e.value;
If onlyIfAbsent is false or the value of an existing node is null, update the value of the current key
if(! onlyIfAbsent || oldValue ==null)
e.value = value;
afterNodeAccess(e);
returnoldValue; }}// Change the number of changes
++modCount;
// Determine whether the current length needs to be expanded
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
Copy the code
Capacity: the resize ()
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
// Get the node array length
int oldCap = (oldTab == null)?0 : oldTab.length;
// Capacity expansion threshold
int oldThr = threshold;
int newCap, newThr = 0;
// Check whether it is the first initialization
if (oldCap > 0) {
// Whether the length of the original node array reaches the maximum length
if (oldCap >= MAXIMUM_CAPACITY) {
// If the length is greater than the maximum, no operation is performed
threshold = Integer.MAX_VALUE;
return oldTab;
}
// Expanded length = original length x 2
// Verify that the new length reaches the maximum length
// Whether the original length is greater than or equal to the default initial length (16)
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
// Threshold after expansion = Original threshold x 2
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
// The original array length is 0. If the expansion threshold is greater than 0, the threshold is assigned to the initialization length of the node array
newCap = oldThr;
else { // zero initial threshold signifies using defaults
// Map with no arguments, initialized for the first time
newCap = DEFAULT_INITIAL_CAPACITY;
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
// If the new threshold is 0, the threshold is recalculated
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
// The threshold is reassigned
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
// Create a new node array
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
if(oldTab ! =null) {
// Migrate the original array data, traverse the original node array
for (int j = 0; j < oldCap; ++j) {
Node<K,V> e;
if((e = oldTab[j]) ! =null) {
// Fetch the data from the original array and set the data at the corresponding index position of the original array to NULL
oldTab[j] = null;
if (e.next == null)
// The current node is a separate node, and the pointer to the next node is null
// Recalculate the hash value and store it in the new node array
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
// Fetch the tree node
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
// The hash value of the node is ampersand to the original length, 0 is the low value, not 0 is the high value
// Divide the new array into two parts according to the original length. The greater part is the high part and the less part is the low part
// Recalculate the linked list in the original array
// Low level list head node, low level list end node
Node<K,V> loHead = null, loTail = null;
// High list head node, high list tail node
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
// Low node data processing
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
// High node data processing
if (hiTail == null)
hiHead = e;
elsehiTail.next = e; hiTail = e; }}while((e = next) ! =null);
// Data is migrated to the new node array
if(loTail ! =null) {
loTail.next = null;
newTab[j] = loHead;
}
if(hiTail ! =null) {
hiTail.next = null;
newTab[j + oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
// Tree node conversion
final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
TreeNode<K,V> b = this;
// Relink into lo and hi lists, preserving order
// Low level node
TreeNode<K,V> loHead = null, loTail = null;
// High level node
TreeNode<K,V> hiHead = null, hiTail = null;
int lc = 0, hc = 0;
// Walk through the tree node, convert the original tree node structure into a linked list structure
for(TreeNode<K,V> e = b, next; e ! =null; e = next) {
next = (TreeNode<K,V>)e.next;
e.next = null;
if ((e.hash & bit) == 0) {
if ((e.prev = loTail) == null)
loHead = e;
else
loTail.next = e;
loTail = e;
++lc;
}
else {
if ((e.prev = hiTail) == null)
hiHead = e;
elsehiTail.next = e; hiTail = e; ++hc; }}// Low list nodes are converted to tree nodes
if(loHead ! =null) {
if (lc <= UNTREEIFY_THRESHOLD)
// The TreeNode Node list is converted to the Node list
tab[index] = loHead.untreeify(map);
else {
// List tree operation
tab[index] = loHead;
if(hiHead ! =null) // (else is already treeified)loHead.treeify(tab); }}if(hiHead ! =null) {
if (hc <= UNTREEIFY_THRESHOLD)
// The TreeNode Node list is converted to the Node list
tab[index + bit] = hiHead.untreeify(map);
else {
// List tree operation
tab[index + bit] = hiHead;
if(loHead ! =null) hiHead.treeify(tab); }}}Copy the code
New tree node: putTreeVal
/ * * * *@paramMap Collection object *@paramTAB node array object *@paramH Hash value of the key *@paramThe key star of the k key-value pair@paramV key value pair value */
final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab, int h, K k, V v) { Class<? > kc =null;
boolean searched = false;
// Get the root node of the treeTreeNode<K,V> root = (parent ! =null)? root() :this;
// Walk through the red black tree
for (TreeNode<K,V> p = root;;) {
int dir, ph;
K pk;
// Compares the hash value of the newly stored node with the hash value of the current tree node
if ((ph = p.hash) > h)
/ / the left subtree
dir = -1;
else if (ph < h)
/ / right subtree
dir = 1;
else
// If the newly stored node is the same as the current tree node, return the node
if((pk = p.key) == k || (k ! =null && k.equals(pk)))
return p;
else
(kc == null && (kc = comparableClassFor(k)) == null)
// kc is of type k
if ((kc == null && (kc = comparableClassFor(k)) == null)
// Compare the type of pk with the type of k, and then compare k with pk
|| (dir = compareComparables(kc, k, pk)) == 0) {
// The current node is a new node
if(! searched) { TreeNode<K,V> q, ch; searched =true;
if(((ch = p.left) ! =null&& (q = ch.find(h, k, kc)) ! =null) || ((ch = p.right) ! =null&& (q = ch.find(h, k, kc)) ! =null))
return q;
}
// Determine whether the new key is in the left or right subtree
dir = tieBreakOrder(k, pk);
}
TreeNode<K,V> xp = p;
// Determine whether to traverse the left or right subtree according to dir
if ((p = (dir <= 0)? p.left : p.right) ==null) {
// The current p node is a leaf node
Node<K,V> xpn = xp.next;
TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
if (dir <= 0)
xp.left = x;
else
xp.right = x;
xp.next = x;
x.parent = x.prev = xp;
if(xpn ! =null)
((TreeNode<K,V>)xpn).prev = x;
// Save the new node to TAB and insert the tree node in balance
moveRootToFront(tab, balanceInsertion(root, x));
return null; }}}// Get the root node
final TreeNode<K,V> root(a) {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
returnr; r = p; }}// Compare k with x
// If x is null or the type of x is different from that of k, 0 is returned; Otherwise return the value of k compared to x
static int compareComparables(Class
kc, Object k, Object x) {
return (x == null|| x.getClass() ! = kc ?0 : ((Comparable)k).compareTo(x));
}
Copy the code
Treeization: treeifyBin
final void treeifyBin(Node<K,V>[] tab, int hash) {
int n, index; Node<K,V> e;
// The node array is null and the node array length is less than 64; Perform capacity expansion.
if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
resize();
// Get node e from node array
else if ((e = tab[index = (n - 1) & hash]) ! =null) {
// first node of the hd tree
TreeNode<K,V> hd = null, tl = null;
// Loop through the list to convert it to a tree node
// The hd is not a tree. It is still a linked list, but each node type in the list is TreeNode
do {
// Convert list E node to tree P node
TreeNode<K,V> p = replacementTreeNode(e, null);
if (tl == null)
// Record the first node of the tree
hd = p;
else {
p.prev = tl;
tl.next = p;
}
tl = p;
// Move the pointer back
} while((e = e.next) ! =null);
// Replace the existing list nodes in the node array with tree nodes
if((tab[index] = hd) ! =null)
// Convert to a red black treehd.treeify(tab); }}TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
return new TreeNode<>(p.hash, p.key, p.value, next);
}
// Convert to a red-black tree
final void treeify(Node<K,V>[] tab) {
TreeNode<K,V> root = null;
for (TreeNode<K,V> x = this, next; x ! =null; x = next) {
next = (TreeNode<K,V>)x.next;
x.left = x.right = null;
if (root == null) {
x.parent = null;
x.red = false;
root = x;
}
else {
K k = x.key;
inth = x.hash; Class<? > kc =null;
for (TreeNode<K,V> p = root;;) {
int dir, ph;
K pk = p.key;
if ((ph = p.hash) > h)
dir = -1;
else if (ph < h)
dir = 1;
else if ((kc == null &&
(kc = comparableClassFor(k)) == null) ||
(dir = compareComparables(kc, k, pk)) == 0)
dir = tieBreakOrder(k, pk);
TreeNode<K,V> xp = p;
if ((p = (dir <= 0)? p.left : p.right) ==null) {
x.parent = xp;
if (dir <= 0)
xp.left = x;
else
xp.right = x;
// Insert a new node and rotate it left or right to make sure the tree is red-black
root = balanceInsertion(root, x);
break;
}
}
}
}
moveRootToFront(tab, root);
}
Copy the code
The remove method
Delete by key: remove(Object key)
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null.false.true)) = =null ?
null : e.value;
}
Copy the code
Remove (Object key, Object value)
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true.true) != null;
}
Copy the code
removeNode(int hash, Object key, Object value,boolean matchValue, boolean movable)
final Node<K,V> removeNode(int hash, Object key, Object value, boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
// Node array exists, corresponding key is not null
if((tab = table) ! =null && (n = tab.length) > 0 && (p = tab[index = (n - 1) & hash]) ! =null) {
Node<K,V> node = null, e; K k; V v;
// Obtain information about the node to be deleted
if(p.hash == hash && ((k = p.key) == key || (key ! =null && key.equals(k))))
node = p;
else if((e = p.next) ! =null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if(e.hash == hash && ((k = e.key) == key || (key ! =null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while((e = e.next) ! =null); }}// Delete the node
if(node ! =null&& (! matchValue || (v = node.value) == value || (value ! =null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
returnnode; }}return null;
}
Copy the code
RemoveTreeNode method
final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab, boolean movable) {
int n;
if (tab == null || (n = tab.length) == 0)
return;
int index = (n - 1) & hash;
TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
if (pred == null)
tab[index] = first = succ;
else
pred.next = succ;
if(succ ! =null)
succ.prev = pred;
if (first == null)
return;
if(root.parent ! =null)
root = root.root();
// The root is null
// movable is true and (root right node is empty or left node is empty or left node is left node is empty)
if (root == null
|| (movable && (root.right == null || (rl = root.left) == null || rl.left == null))) {
// Remove the tree operation
tab[index] = first.untreeify(map); // too small
return;
}
TreeNode<K,V> p = this, pl = left, pr = right, replacement;
if(pl ! =null&& pr ! =null) {
TreeNode<K,V> s = pr, sl;
while((sl = s.left) ! =null) // find successor
s = sl;
boolean c = s.red;
s.red = p.red;
p.red = c; // swap colors
TreeNode<K,V> sr = s.right;
TreeNode<K,V> pp = p.parent;
if (s == pr) { // p was s's direct parent
p.parent = s;
s.right = p;
}
else {
TreeNode<K,V> sp = s.parent;
if((p.parent = sp) ! =null) {
if (s == sp.left)
sp.left = p;
else
sp.right = p;
}
if((s.right = pr) ! =null)
pr.parent = s;
}
p.left = null;
if((p.right = sr) ! =null)
sr.parent = p;
if((s.left = pl) ! =null)
pl.parent = s;
if ((s.parent = pp) == null)
root = s;
else if (p == pp.left)
pp.left = s;
else
pp.right = s;
if(sr ! =null)
replacement = sr;
else
replacement = p;
}
else if(pl ! =null)
replacement = pl;
else if(pr ! =null)
replacement = pr;
else
replacement = p;
if(replacement ! = p) { TreeNode<K,V> pp = replacement.parent = p.parent;if (pp == null)
root = replacement;
else if (p == pp.left)
pp.left = replacement;
else
pp.right = replacement;
p.left = p.right = p.parent = null;
}
TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);
if (replacement == p) { // detach
TreeNode<K,V> pp = p.parent;
p.parent = null;
if(pp ! =null) {
if (p == pp.left)
pp.left = null;
else if (p == pp.right)
pp.right = null; }}if (movable)
moveRootToFront(tab, r);
}
Copy the code
Disturbance function: tableSizeFor
// Compute a value greater than the current value to the lowest power of 2
// graphic: perturbation function.png
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;
}
Copy the code
LinkedHashMap
attribute
// Two-way list header
transient LinkedHashMap.Entry<K,V> head;
// The end of the bidirectional list
transient LinkedHashMap.Entry<K,V> tail;
// The hash map of this link is iteratively sorted:
// true for access order,
// false is used for insertion order.
final boolean accessOrder;
Copy the code
Defined data structures
Entry
// A custom structure type that inherits the Node type in HashM
static class Entry<K.V> extends HashMap.Node<K.V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next); }}Copy the code
Initialize the
// No arguments
public LinkedHashMap(a) {
super(a); accessOrder =false;
}
// Initialize the length
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
// Initialize the length and load factor
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
// Initialize the length, load factor, and type of iteration sort
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
// The parameter is the initialization method of the collection
public LinkedHashMap(Map<? extends K, ? extends V> m) {
super(a); accessOrder =false;
putMapEntries(m, false);
}
Copy the code
Put method and remove method
The same as the put and remove (Object key) methods of HashMap
// Inherit the HashMap put method and remove (Object key) method,
// Override the newNode method,
// the newTreeNode method
// afterNodeAccess,
// afterNodeInsertion method
// visting deremoval,
/ / replacementTreeNode method
Copy the code
NewNode and newTreeNode
// Create a node
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
LinkedHashMap.Entry<K,V> p =
new LinkedHashMap.Entry<K,V>(hash, key, value, e);
linkNodeLast(p);
return p;
}
// newTreeNode in putTreeVal
TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
linkNodeLast(p);
return p;
}
// Save the new node to the bidirectional linked list
private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
LinkedHashMap.Entry<K,V> last = tail;
tail = p;
if (last == null)
head = p;
else{ p.before = last; last.after = p; }}Copy the code
afterNodeAccess
// Move the current node to the end of the list
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
if(accessOrder && (last = tail) ! = e) { LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after; p.after =null;
if (b == null)
head = a;
else
b.after = a;
if(a ! =null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else{ p.before = last; last.after = p; } tail = p; ++modCount; }}Copy the code
afterNodeInsertion
// Delete the header
// This should be an extended operation
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// Evict is false when initialized; True is called when new
// Determine whether to remove the header
if(evict && (first = head) ! =null && removeEldestEntry(first)) {
K key = first.key;
// Delete a node
removeNode(hash(key), key, null.false.true); }}protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
Copy the code
afterNodeRemoval
// Delete a node from the bidirectional list
// Delete the deleted nodes in the extra maintained linked list
void afterNodeRemoval(Node<K,V> e) { // unlink
LinkedHashMap.Entry<K,V> p = (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.before = p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a == null)
tail = b;
else
a.before = b;
}
Copy the code
replacementTreeNode
// Replace with a tree node
TreeNode<K,V> replacementTreeNode(Node<K,V> p, Node<K,V> next) {
LinkedHashMap.Entry<K,V> q = (LinkedHashMap.Entry<K,V>)p;
TreeNode<K,V> t = new TreeNode<K,V>(q.hash, q.key, q.value, next);
transferLinks(q, t);
return t;
}
// Replace SRC with DST
private void transferLinks(LinkedHashMap.Entry<K,V> src, LinkedHashMap.Entry<K,V> dst) {
LinkedHashMap.Entry<K,V> b = dst.before = src.before;
LinkedHashMap.Entry<K,V> a = dst.after = src.after;
if (b == null)
head = dst;
else
b.after = dst;
if (a == null)
tail = dst;
else
a.before = dst;
}
Copy the code
traverse
LinkedHashMap maintains an additional two-way linked list to retrieve stored element data in an orderly fashion as it traverses.
Set
HashSet
attribute
// Set set holds the structural type of data
private transient HashMap<E,Object> map;
// The value of key-value in the map object
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
Copy the code
Initialize the
// No arguments
public HashSet(a) {
map = new HashMap<>();
}
// Specify the initialization length
public HashSet(int initialCapacity) {
map = new HashMap<>(initialCapacity);
}
// Specify the initialization length and load factor
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
// specify the initialization length and loadFactor and Boolean values to distinguish the HashSet(int initialCapacity, float loadFactor) constructor
// (This package private constructor is only used by LinkedHashSet.)
// The sequence method creates a LinkedHashMap object to ensure orderliness
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
// The argument is a collection constructor
public HashSet(Collection<? extends E> c) {
// Determine the maximum length of initialization according to the length of the collection
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1.16));
// Save the collection to map
addAll(c);
}
Copy the code
The add method
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Copy the code
The remove method
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
Copy the code
LinkedHashSet
Initialize the
Int initialCapacity (int initialCapacity, float loadFactor, Boolean dummy);
// Create a LinkedHashMap object to ensure orderliness
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f.true);
}
public LinkedHashSet(a) {
super(16..75f.true);
}
public LinkedHashSet(Collection<? extends E> c) {
super(Math.max(2*c.size(), 11), .75f.true);
addAll(c);
}
Copy the code