Set, List, Map, and setmap in Multi-learning Java
For a long time, the List of data that has been used in the Code is mainly List, and it is all ArrayList. This is enough. ArrayList is a packaging tool class used to implement dynamic arrays, so that code can be pulled in and pulled out during code writing, and iterative traversal is quite convenient. I also don't know when the tool classes such as HashMap and HashSet will often appear in the code. It should be said that there are more hashmaps, and they are still typical questions for interviews. We will also take a look at them at ordinary times. It is easy to get started with a table that corresponds to a key value. After a deep understanding, I found that this stuff has some minor mysteries. Especially after the new JDK version changes HashMap into a tree, the Code is a little complicated. Set is rarely used at the beginning, but a TreeSet is accidentally found in a code. It is quite interesting to find that this class can bring its own smoothness. This is also a good tool. When the code is written too much, I feel the importance of the Foundation. Therefore, I would like to write a small article here to briefly organize some knowledge about the set.Now, let's make a simple arrangement:
- List: a List. It supports arrays and linked lists, and is generally linear.
- Map: A ing table stores the correspondence between keys and values.
- Set: indicates a Set. It is mainly used to sort and sort duplicate data.
Let's take a look at the ListList is a window used to store linear data, such as ArrayList for arrays and List for linked lists.
ArrayListThis is an array List, but it provides the automatic resizing function to implement the List interface. external operations are all accessed through the interface declaration method, which is safe and convenient. The key to ArrayList is automatic resizing. You can set the initial capacity during object initialization or by default. If the array size is not explicitly specified, you can choose not to specify the initial size. If so, you can specify a size to reduce the lag caused by dynamic resizing. Speaking of this, let's talk about how resizing is implemented. Let's look at the following code:
1234567891011 |
private void grow( int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; 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); } |
Grow is a method that is triggered when the ArrayList is added to an element or when it is easy to check. Main process: 1. Get the length of the array and shift it right. This is equivalent to oldCapacity/2, obtain the new length 2. If the length is smaller than the minimum capacity, use the minimum capacity 3. If the length is greater than the maximum capacity, use the maximum capacity. A hugeCapacity method is called here, minCapacity and MAX_ARRAY_SIZE are compared. If minCapacity is greater than MAX_ARRAY_SIZE, Integer is used. MAX_VALUE; otherwise, MAX_ARRAY_SIZE is used. Interestingly, MAX_ARRAY_SIZE is Integer. MAX_VALUE-8; I don't know what this means. 4. Finally, I will call a replication method to copy the existing number to a new array. Because of this replication process, if the array is relatively large, the expansion will always be triggered, and of course there will be a choppy situation. Therefore, if you know the maximum value at the beginning and it is easy to increase to this value, you can specify the size at the beginning of initialization.
ShortlistThis is a tool class for linked lists. It is better to add or delete a linked list, but it is slower to search. As for the code, there seems to be nothing special, that is, a string of pointer links. Of course, in Java, objects are used instead to create a Node object, the Node itself points to the previous Node and the next Node, which is the structure of the linked list:
1234567891011 |
private static class Node<E> { E item; Node<E> next; Node<E> prev; Node(Node<E> prev, E element, Node<E> next) { this .item = element; this .next = next; this .prev = prev; } } |
Then, point the two nodes to the header and the end. The following code:
12345678910111213 |
/** * Pointer to first node. * Invariant: (first == null && last == null) || * (first.prev == null && first.item != null) */ transient Node<E> first; /** * Pointer to last node. * Invariant: (first == null && last == null) || * (last.next == null && last.item != null) */ transient Node<E> last; |
Take a look at the add operation:
1234567891011121314 |
/** * Links e as last element. */ 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++; } |
The past is: 1. Get the final Node and put it in l. 2. Create a new Node and get the data to this Node. During the creation process, the prev of the new Node is directed to l, in this way, link 3 is connected, and the last point is directed to this new Node4. Then, judge whether l is null. If it is null, it indicates that it is an empty linked list, and the new node is the first element, in this way, the first Node also points to newNode5. If it is not empty, the next Node of l points to newNode6, And the delete operation of the accumulative counter also points to the moving operation of the front and back nodes of this Node.
Let's take a look at Map.Map is an application of key-value ing tables. Its main implementation classes include HashMap, HashTable, and TreeMap.
HashMap and HashTableHashMap is used for key-value ing using the hash algorithm. HashTable is a class with synchronous thread security. The main difference between them is this. The principle is similar. It is implemented by combining buckets and chains. The bucket is used to store the Key, and the value needs to be stored in a linked list due to Hash collision.
- BucketIt is efficient and can be located in one step through Hash computing.
- Linked ListThe significance is to access repeated hash data.
I wrote a learning Note: Hashtable and HashMap before, but I only saw that the HashMap of JDK1.8 has changed its storage structure and adopts the structure of a red/black tree, this may solve the linked list search efficiency problem, right? There is no detailed study.
TreeMapAfter reading the TreeMap code, I found that the tree structure is still used, and the tree is red and black. Because the red and black trees are ordered, they naturally carry the sorting function. Of course, you can also use comparator to specify a comparison method to achieve specific sorting. Because the tree structure is used for storage, adding and deleting data may be difficult. Let's take a look at the put code:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152 |
public V put(K key, V value) { Entry<K,V> t = root; if (t == null ) { compare(key, key); // type (and possibly null) check root = new Entry<>(key, value, null ); size = 1 ; modCount++; return null ; } int cmp; Entry<K,V> parent; // split comparator and comparable paths Comparator<? super K> cpr = comparator; if (cpr != null ) { do { parent = t; cmp = cpr.compare(key, t.key); if (cmp < 0 ) t = t.left; else if (cmp > 0 ) t = t.right; else return t.setValue(value); } while (t != null ); } else { if (key == null ) throw new NullPointerException(); @SuppressWarnings ( "unchecked" ) Comparable<? super K> k = (Comparable<? super K>) key; do { parent = t; cmp = k.compareTo(t.key); if (cmp < 0 ) t = t.left; else if (cmp > 0 ) t = t.right; else return t.setValue(value); } while (t != null ); } Entry<K,V> e = new Entry<>(key, value, parent); if (cmp < 0 ) parent.left = e; else parent.right = e; fixAfterInsertion(e); size++; modCount++; return null ; } |
1. Check whether the root node exists. If the root node does not exist, it indicates that the first data is used as the root node of the tree. 2. check whether a comparator exists. If yes, use the comparator to find the data storage location, if the result returned by the comparator is less than 0, the result is left, the result is greater than 0, and the result is right. Otherwise, the current node value is directly replaced. 3. If the comparator does not exist, the key is directly compared with the node key, compare with the previous method 4. Next, create a subnode on the found parent, and put it in the Left or Right subnode. 5. fixAfterInsertion is used to color the node. 6. It will be a little troublesome for The accumulators to process the remove operation. Besides deleting data, we also need to rebalance the red and black trees. In addition, TreeMap implements the NavigableMap <K, V> interface, so it also provides some return operations for the data set.
Finally, let's take a look at the SetSet mainly applies to two types of applications: HashSet and TreeSet.
HashSetThe literal meaning is very clear, and the Hash set is used. This set is characterized by the use of the Hash algorithm to store data, so the data is not repeated, and access is relatively fast. How can we do this?
1234 |
public boolean add(E e) { return map.put(e, PRESENT)== null ; } |
It turns out that there is a map object. What is map?
1 |
private transient HashMap<E,Object> map; |
It is a HashMap. If you understand HashMap, you will understand that such data will not be repeated. Because the stored object itself is saved as the Key, only one copy will exist in HashMap. I understand this other things very well.
TreeSetThis set is used to sort the set, that is, in addition to the ability to weight the set, it can also bring its own sorting function. After reading the TreeSet code, we found that it is implemented on the basis of TreeMap. More accurately, it should be the derived class of NavigableMap. TreeSet is based on TreeMap if map is not specified by default.
123 |
public TreeSet() { this ( new TreeMap<E,Object>()); } |
So here, we may be more concerned about how TreeSet sorts out duplicates? Let's take a look at the add method:
123 |
public boolean add(E e) { return m.put(e, PRESENT)== null ; } |
Similar to HashSet, it is implemented based on Map features. It is indeed simple and effective.