Set
Set focuses on the unique nature . This system set is used to store unordered elements ( the order of deposit and withdrawal is not necessarily the same ) , and the value cannot be repeated. right
The essence of object equality is judged by the object hashCode value ( java calculates this sequence number based on the memory address of the object), if you want
To make two different objects equal, you must override the Object 's hashCode method and equals method. HashSet ( Hash table)
The side of the hash table stores the hash value. The order in which HashSet stores elements is not in the order in which they were stored ( obviously different from List ) but
It is stored according to the hash value, so the fetched data is also obtained according to the hash value. The hash value of the element is obtained through the hashcode method of the element
Yes , HashSet first judges the hash value of two elements, if the hash value is the same, then compares the equals method if the result of equls is
true , the HashSet is treated as the same element. Not the same element if equals is false .
How to store the elements with the same hash value equals to false , that is, to postpone under the same hash value (you can think of elements with the same hash value
element in a hash bucket). That is, store a column like a hash. Figure 1 shows the situation where the hashCode values are different; Figure 2 shows
The hashCode values are the same, but the equals are not the same.
HashSet determines the location of elements in memory by hashCode value. Multiple elements can be stored in one hashCode position.
TreeSet (binary tree)
1. TreeSet() uses the principle of binary tree to sort the objects of the new add() in the specified order (ascending order, descending order), each additional
Objects are sorted and inserted into the specified position of the binary tree.
2. Both Integer and String objects can be sorted by the default TreeSet , but objects of custom classes are not allowed, and you can define them yourself
The class must implement the Comparable interface and override the corresponding compareTo() function before it can be used normally.
3. When overriding the compare() function, it is necessary to return the corresponding value to make the TreeSet sorted according to certain rules
4. Compares the order of this object with the specified object. If the object is less than, equal to, or greater than the specified object, returns a negative integer, zero, or
positive integer.
LinkHashSet ( HashSet+LinkedHashMap ) For LinkedHashSet, it inherits from HashSetand isimplemented LinkedHashMap
The bottom layer of LinkedHashSet uses LinkedHashMap to save all elements, it inherits from HashSet , and all its methods operate
It is the same as HashSet , so the implementation of LinkedHashSet is very simple, only four construction methods are provided, and by passing a
An identification parameter, calls the constructor of the parent class, and constructs a LinkedHashMap at the bottom layer to realize it, and it is related to the parent class in related operations
The operation of HashSet is the same, just call the method of the parent class HashSet directly .
Map
HashMap (array + linked list + red-black tree)
HashMap stores data according to the hashCode value of the key . In most cases, its value can be directly located, so it has fast access.
Ask about speed, but the order of traversal is uncertain. HashMap only allows the key of one record to be null at most , and the value of multiple records is allowed
null . HashMap is not thread-safe, that is, multiple threads can write HashMap at the same time at any time , which may cause data inconsistency
Sincerely. If you need to meet thread safety, you can use the synchronizedMap method of Collections to make HashMap thread-safe
capabilities, or use ConcurrentHashMap . We use the following picture to introduce
The structure of HashMap .
JAVA7 implementation
In the general direction, HashMap is an array, and each element in the array is a one-way linked list. In the figure above, each green
The entity is an instance of the nested class Entry , and Entry contains four attributes: key, value, hash value and next for a one-way linked list .
1. capacity : The current array capacity is always 2^n , and can be expanded. After expansion, the array size is twice the current size .
2. loadFactor : load factor, the default is 0.75 . 3. threshold : The threshold of expansion, equal to capacity * loadFactor
JAVA8 implementation
Java8 has made some modifications to HashMap . The biggest difference is the use of red-black tree, so it is composed of array + linked list + red-black tree
become.
According to the introduction of Java7 HashMap , we know that when searching, we can quickly locate the specific location of the array according to the hash value.
mark, but after that, we need to compare one by one along the linked list to find what we need. The time complexity depends on
Due to the length of the linked list, it is O(n) . In order to reduce the overhead of this part, in Java8 , when the number of elements in the linked list exceeds 8 , the
The linked list is converted into a red-black tree, and the time complexity can be reduced to O(logN) when searching in these positions .
ConcurrentHashMap
Segment segment
The ideas of ConcurrentHashMap and HashMap are similar, but because it supports concurrent operations, it is more complicated. all
A ConcurrentHashMap is composed of Segments , and Segment means " part " or " a section " , so many
Both parties will describe it as a segment lock. Note that in the text, I used " slot " to represent a segment in many places .
Thread safety ( Segment inherits ReentrantLock and locks)
A simple understanding is that ConcurrentHashMap is a Segment array, and Segment is implemented by inheriting ReentrantLock
Rows are locked, so each operation that needs to be locked locks a segment , so as long as each segment is thread-safe
, it also achieves global thread safety. Parallelism concurrencyLevel : parallel level, concurrency number, segment number, how to translate is not important, understand it. The default is 16 ,
That is to say, ConcurrentHashMap has 16 Segments , so in theory, at this time, it can support up to 16 segments at the same time
Threads write concurrently, as long as their operations are distributed on different segments . This value can be set to other values during initialization
value, but once initialized, it cannot be expanded. And then specifically to the interior of each segment , in fact, each segment is very similar
The HashMap introduced before , but it must ensure thread safety, so it is more troublesome to deal with.
Java8 implementation (introduced red-black tree)
Java8 has made relatively large changes to ConcurrentHashMap , and Java8 has also introduced red-black trees.
Java8 implementation (../../../../../0 horse soldier / new folder /BAT interview assault data (1)/ organization /BAT interview assault data /06-JAVA interview
Collation of core knowledge points ( students with more time to review comprehensively ).assets/Java8 implementation ( introduced red-black tree ).jpg)
HashTable (thread-safe)
Hashtable is a legacy class, and many common functions of mapping are similar to HashMap , the difference is that it inherits from the Dictionary class, and is
Thread-safe, only one thread can write Hashtable at any time , and the concurrency is not as good as ConcurrentHashMap , because
ConcurrentHashMap introduces segment locks. Hashtable is not recommended to be used in new code, it can be used when thread safety is not required
Replace it with HashMap , and use ConcurrentHashMap when thread safety is required .
3.4.4. TreeMap (sortable)
TreeMap implements the SortedMap interface, which can sort the records it saves according to the key. The default is the ascending order of the key value, and it can also be
Specify the comparator for sorting. When traversing the TreeMap with Iterator , the records obtained are sorted. If using a sorted map,
It is recommended to use TreeMap .
When using TreeMap , the key must implement the Comparable interface or pass in a custom one when constructing the TreeMap
Comparator , otherwise an exception of type java.lang.ClassCastException will be thrown at runtime .
LinkHashMap (record insertion order)
LinkedHashMap is a subclass of HashMap , which saves the insertion order of records, and uses Iterator to traverse
When LinkedHashMap is used, the record obtained first must be inserted first, and it can also be constructed with parameters and sorted according to the access order. The implementation of TreeMap is a red-black tree data structure, that is to say, a self-balancing sorted binary tree, so that it can ensure fast
Retrieves the specified node.
The relationship between TreeSet and TreeMap
In order to let everyone understand the relationship between TreeMap and TreeSet , let's look at part of the source code of the TreeSet class:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
// Use the key of NavigableMap to save the elements of the Set collection
private transient NavigableMap<E,Object> m;
// Use a PRESENT as all values of the Map collection .
private static final Object PRESENT = new Object();
// Constructor for package access rights, create a Set collection with the specified NavigableMap object
TreeSet(NavigableMap<E,Object> m)
{
this.m = m;
}
public TreeSet() // ①
{
// Create a new TreeMap in natural order ,
// Create a TreeSet based on the TreeSet ,
// Use the key of the TreeMap to save the elements of the Set collection
this(new TreeMap<E,Object>());
}
public TreeSet(Comparator<? super E> comparator) // ②
{
// Create a new TreeMap with custom sorting ,
// Create a TreeSet based on the TreeSet ,
// Use the key of the TreeMap to save the elements of the Set collection
this(new TreeMap<E,Object>(comparator));
}
public TreeSet(Collection<? extends E> c)
{
// Call the No. ① constructor to create a TreeSet , and the bottom layer uses TreeMap to save the collection elements
this();
// Add all elements in the Collection collection c to the TreeSet
addAll(c);
}
public TreeSet(SortedSet<E> s)
{
// Call the No. ② constructor to create a TreeSet , and the bottom layer uses TreeMap to save the collection elements
this(s.comparator());
// Add all elements in the SortedSet collection s to the TreeSet
addAll(s);
}
// Other methods of TreeSet just call the method of TreeMap directly to provide implementation
...
public boolean addAll(Collection<? extends E> c)
{
if (m.size() == 0 && c.size() > 0 &&
c instanceof SortedSet &&
m instanceof TreeMap)
{// Forcibly convert the c collection to the SortedSet collection
SortedSet<? extends E> set = (SortedSet<? extends E>) c;
// Forcibly convert the m collection to a TreeMap collection
TreeMap<E,Object> map = (TreeMap<E, Object>) m;
Comparator<? super E> cc = (Comparator<? super E>) set.comparator();
Comparator<? super E> mc = map.comparator();
// If the two Comparators cc and mc are equal
if (cc == mc || (cc != null && cc.equals(mc)))
{
// Add all the elements in the Collection as the key of the TreeMap collection
map.addAllForTreeSet(set, PRESENT);
return true;
}
}
// Directly call the addAll() method of the parent class to implement
return super.addAll(c);
}
...
}
display more
As can be seen from the above code, the ① and ② constructors of TreeSet both create a new TreeMap as the actual storage of Set elements
container, while the other two constructors depend on constructors ① and ② respectively. It can be seen that the storage actually used at the bottom of the TreeSet
The storage container is TreeMap .
Completely similar to HashSet , most of the methods in TreeSet are implemented by directly calling the methods of TreeMap .
Readers can refer to the source code of TreeSet by themselves , so it will not be given here.
For TreeMap , it uses a sorted binary tree called " red-black tree " to save each Entry in the Map - each
Entry is treated as a node of the " red-black tree " . For example, for the following program:
public class TreeMapTest
{
public static void main(String[] args)
{
TreeMap<String , Double> map =
new TreeMap<String , Double>();
map.put("ccc" , 89.0);
map.put("aaa" , 80.0);
map.put("zzz" , 80.0);
map.put("bbb" , 89.0);
System.out.println(map);
}
}
display more
When the program executes map.put(“ccc”, 89.0);, the system will directly put the Entry “ccc”-89.0 into the Map , and this Entry
It is the root node of the " red-black tree " . Then the program executes map.put(“aaa”, 80.0);, the program will add “aaa”-80.0 as a new node
Add to the existing red-black tree.
In the future, every time a key-value pair is added to the TreeMap , the system needs to treat the Entry as a new node and add it to the existing red and black nodes.
In the tree, in this way, it can be guaranteed that all keys in the TreeMap are always arranged from small to large. For example, if we output the above program, the
See the following results (all keys are arranged from small to large):
{aaa=80.0, bbb=89.0, ccc=89.0, zzz=80.0} Show more
Adding Nodes to TreeMap
For TreeMap , because it uses a " red-black tree " at the bottom to save the Entry in the collection , it means that the TreeMap adds elements
The performance of extracting elements and extracting elements is lower than that of HashMap : when TreeMap adds elements, it needs to loop through to find the insertion of the newly added Entry
position, so it consumes more performance; when taking out elements from the TreeMap , it needs to go through a loop to find the appropriate Entry , which is also more expensive
performance. But the advantages of TreeMap and TreeSet over HashMap and HashSet are: All entries in TreeMap are always pressed
The key is kept in an ordered state according to the specified sorting rule, and all elements in the TreeSet are always kept in an ordered state according to the specified sorting rule.
red black tree
A red-black tree is a self-balancing sorted binary tree. The value of each node in the tree is greater than or equal to the value of all nodes in its left subtree.
value, and less than or equal to the value of all nodes in its right subtree, which ensures that the red-black tree runtime can quickly search in the tree
and locate the desired nodes.
In order to understand the underlying implementation of TreeMap , we must first introduce the two data structures of sorted binary tree and red-black tree. Among them, the red-black tree is another
A special kind of sorted binary tree.
Sorted binary tree is a special structure of binary tree, which can sort and retrieve all nodes in the tree very conveniently.
A sorted binary tree is either an empty binary tree or a binary tree with the following properties:
If its left subtree is not empty, the values of all nodes on the left subtree are less than the value of its root node;
If its right subtree is not empty, the values of all nodes on the right subtree are greater than the value of its root node;
Its left and right subtrees are also sorted binary trees.
Figure 1 shows a sorted binary tree:
Figure 1. Sorting binary tree For sorting a binary tree, if you traverse in order, you can get an ordered sequence from small to large. As 1, the binary tree can be traversed in order:
{2,3,4,8,9,9,10,13,15,18}
The steps of creating a sorted binary tree, that is, the process of continuously adding nodes to the sorted binary tree, the steps of adding nodes to the sorted binary tree are as follows
Down:
1. Start the search with the current node of the root node.
2. Compare the value of the new node with the value of the current node.
3. If the value of the new node is larger, use the right child node of the current node as the new current node; if the value of the new node is smaller, use the current node
The left child of the previous node becomes the new current node.
4. Repeat steps 2 and 3 until a suitable leaf node is found.
5. Add the new node as a child node of the leaf node found in step 4 ; if the new node is larger, add it as the right child node; otherwise add
is the left child node.
After mastering the above theory, let's analyze the process of adding nodes to TreeMap ( Entry internal class is used to represent nodes in TreeMap )
Implementation, the put(K key, V value) method of the TreeMap collection realizes putting the Entry into the sorted binary tree. The source of the method is as follows
code:
public V put(K key, V value)
{
// First save the root node of the linked list with t
Entry<K,V> t = root;
// If t==null , it indicates an empty linked list, that is, there is no Entry in the TreeMap
if (t == null)
{
// Create an Entry with the new key-value , and use the Entry as root
root = new Entry<K,V>(key, value, null);
// Set the size of the Map collection to 1 , which means it contains an Entry
size = 1;
// Record number of modifications is 1
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
Comparator<? super K> cpr = comparator;
// If the comparator cpr is not null , it means that the custom sorting is adopted
if (cpr != null)
{
do {
// Use the Entry referenced by t after the last cycle of the parent
parent = t;
// Compare the newly inserted key with the key of t
cmp = cpr.compare(key, t.key);
// If the newly inserted key is less than the key of t , t is equal to the left node of t
if (cmp < 0)
t = t.left;
// If the newly inserted key is greater than the key of t , t is equal to the right node of t
else if (cmp > 0)
t = t.right;
// If the two keys are equal, the new value overwrites the original value ,
// and return the original value
else
return t.setValue(value);
} while (t != null); show more
The code in bold in the above program is the key algorithm to realize the " sorting binary tree " . Whenever the program wants to add a new node: the system always starts from the tree
The root node of the current node is compared - that is, the root node is regarded as the current node, if the new node is greater than the current node, and the right child of the current node
If the point exists, the right child node is taken as the current node; if the newly added node is smaller than the current node, and the left child node of the current node exists, then
Take the left child node as the current node; if the new node is equal to the current node, overwrite the current node with the new node, and end the loop——
Until the left and right child nodes of a certain node do not exist, add a new node to the child node of the node - if the new node is larger than the node,
is added as the right child; if the new node is smaller than this node, it is added as the left child.
Delete node of TreeMap
When a program deletes a node from a sorted binary tree, in order to keep it as a sorted binary tree, the program must
for maintenance. Maintenance can be divided into the following situations:
(
1 ) The node to be deleted is a leaf node, you only need to delete it from its parent node.
(
2 ) The deleted node p has only the left subtree, just add the left subtree pL of p to the left subtree of the parent node of p ; the deleted node p only has
If there is a right subtree, just add p 's right subtree pR to the right subtree of p 's parent node.
(
3 ) If the left and right subtrees of the deleted node p are not empty, there are two ways:
}
else
{
if (key == null)
throw new NullPointerException();
Comparable<? super K> k = (Comparable<? super K>) key;
do {
// Use the Entry referenced by t after the last cycle of the parent
parent = t;
// Compare the newly inserted key with the key of t
cmp = k.compareTo(t.key);
// If the newly inserted key is less than the key of t , t is equal to the left node of t
if (cmp < 0)
t = t.left;
// If the newly inserted key is greater than the key of t , t is equal to the right node of t
else if (cmp > 0)
t = t.right;
// If the two keys are equal, the new value overwrites the original value ,
// and return the original value
else
return t.setValue(value);
} while (t != null);
}
// Make the newly inserted node a child node of the parent node
Entry<K,V> e = new Entry<K,V>(key, value, parent);
// If the newly inserted key is less than the key of the parent , then e is the left child node of the parent
if (cmp < 0)
parent.left = e;
// If the newly inserted key is smaller than the parent 's key , then e is the right child node of the parent
else
parent.right = e;
// Repair the red-black tree
fixAfterInsertion(e); // ①
size++;
modCount++;
return null;
} Let pL be the left or right child of p 's parent q (depending on whether p is the left or right child of its parent q ), and pR be
The right child node of the inorder predecessor node s of the p node ( s is the bottom right node of pL , that is, the largest node in the pL subtree).
Replace the node pointed to by p with the in-order predecessor or successor of p node, and then delete the in-order predecessor or successor node from the original sorted binary tree.
Can. (That is, replace the p node with the smallest node larger than p or the largest node smaller than p ).
Figure 2 shows a schematic diagram of the deleted node with only the left subtree:
Figure 2. The deleted node has only the left subtree
Figure 3 shows a schematic diagram of the deleted node with only the right subtree:
Figure 3. The deleted node has only the right subtree
Figure 4 shows the situation that the deleted node has both left child nodes and right child nodes. At this time, we use the first method for maintenance:
Figure 4. The deleted node has both a left subtree and a right subtree
Figure 5 shows the situation where the deleted node has both a left subtree and a right subtree. At this time, we use the second method for maintenance: Figure 5. The deleted node has both a left subtree and a right subtree Tree
TreeMap deleted nodes are maintained using the situation on the right as shown in Figure 5 — that is, the smallest node in the right subtree of the deleted node and the deleted node are used for maintenance.
It is maintained by deleting nodes and exchanging them.
The method of deleting nodes in TreeMap is implemented by the following method:
private void deleteEntry(Entry<K,V> p)
{
modCount++;
size--;
// If the left subtree and right subtree of the deleted node are both empty
if (p.left != null && p.right != null)
{
// Replace the p node with the inorder successor node of the p node
Entry<K,V> s = successor (p);
p.key = s.key;
p.value = s.value;
p = s;
}
// If the left node of the p node exists, replacement represents the left node; otherwise, it represents the right node.
Entry<K,V> replacement = (p.left != null ? p.left : p.right);
if (replacement != null)
{
replacement.parent = p.parent;
// If p has no parent node, replacemment becomes the parent node
if (p.parent == null)
root = replacement;
// If the p node is the left child of its parent node
else if (p == p.parent.left)
p.parent.left = replacement;
// If the p node is the right child of its parent node
else
p.parent.right = replacement;
p.left = p.right = p.parent = null;
// Repair the red-black tree
if (p.color == BLACK)
fixAfterDeletion(replacement); // ①
}
// If the p node has no parent node, show more