jdk1.8.0_144 Download address: //www.VeVB.COM/softs/551512.html
AbstractMap abstract class implements some simple and general methods, which is not difficult in itself. But there are two methods in this abstract class that are worth paying attention to. The implementation of the keySet and values method source code can be said to be textbook-style models.
Abstract classes are usually implemented as a skeleton to implement common methods for their respective subclasses. In the previous article, we explained the Map interface, and this article analyzes and studies the AbstractMap abstract class.
There are quite a lot of Map type data structures in Java. AbstractMap, as their skeleton implementation, implements some methods of the Map interface, that is, it provides public methods for its subclasses, and various Map without implementation may be different.
An abstract class cannot directly create instances of abstract classes through the new keyword, but it can have constructors. AbstractMap provides a protected modified parameterless constructor, meaning that only its subclass can access (of course it is an abstract class itself, and other classes cannot be instantiated directly), that is, only its subclass can call this parameterless constructor.
An Entry interface is defined internally in the Map interface. This interface is an internal implementation of the Map map to maintain a key-value key-value pair, and the key-value is stored in this Map.Entry. AbstractMap implements this internal interface, there are two in total: one is the mutable SimpleEntry and the other is the immutable SimpleImmutableEntry.
public static class SimpleEntry<K,V> implements Entry<K,V>, java.io.Serializable
The Map.Entry<K, V> interface is implemented, and Serializable (can be serialized).
Its method is relatively simple, all of which are operations of taking values and storing values. The definition of key value is a final modification, which means it is an immutable reference. In addition, its setValue method is slightly special. The value stored is not the stored value, but the old value returned. What you need to learn is the equals and hashCode methods it rewrites.
public boolean equals(Object o) { if (!(o instanceof Map.Entry)) //To determine whether the parameters are of Map.Entry type, if equals are equal, the first thing to do is to be the same type return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; //Forcing the Object type to Map.Entry type, the parameter uses "?" instead of "K, V" because the type of the generic will be erased at runtime. The compiler does not know what type K and V is, return eq(key, e.getKey()) && eq(value, e.getValue()); //Key and value call the eq method for judgment respectively, and equals are equal when both return ture. } private static boolean eq(Object o1, Object o2) {return o1 == null ? o2 == null : o1.equals(o2); //This three-item operator is also very simple, but it should be noted that although o1 and o2 are Object types here, the equals method of Object type is referenced by "==", so don't think there is a problem here, because in reality, the o1 type may be String, although it is converted to Object, so the String#equals method is still called when calling the equals method. }To correctly rewrite the equals method and be used correctly, you usually need to rewrite the hashCode method.
public int hashCode() {return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); // When the values of key and value are not null, the hashCode will be XORed. }public static class SimpleImmutableEntry<K,V> implements Entry<K,V>, java.io.Serializable SimpleImmutableEntry
Entry defined as immutable is actually immutable because it does not provide the setValue method, and naturally cannot be modified through the setValue method when multiple threads are accessed simultaneously. Compared to SimpleEntry, its key and value member variables are defined as final types. Calling the setValue method will throw an UnsupportedOperationException exception.
Its equals and hashCode methods are consistent with SimpleEntry.
Next, check which methods in the Map interface are implemented by AbstractMap abstract class.
public int size()
A entrySet method is defined in the Map, which returns the Set collection of Map.Entry. The size method of the Set collection is called directly, which is the size of the Map.
public boolean isEmpty()
Call the above size method, equal to 0, it is empty.
public boolean containsKey(Object key)
The implementation of this method is relatively simple. By calling the entrySet method, iterator of the Set collection is obtained and traversed with the parameter key. Map can be stored as null's key value. Since key=null is stored in Map with special special storage (the hashCode value cannot be calculated), we have also done a way to determine whether the parameter key is empty.
public boolean containsValue(Object value)
This method implementation is consistent with containsKey.
public V get(Object key)
This method implementation is similar to the above two, the difference is that the above is equal to the boolean, and this method returns the value.
public V put(K key, V value)
The method of storing key-value pairs into the Map is not implemented in detail, and an UnsupportedOperationException will be directly thrown.
public V remove(Object key)
Delete the key-value key-value pair specified in the Map via the parameter key. This method is also very simple. It also traverses the Set collection of Map.Entry through an iterator, finds the corresponding key value, and deletes Map.Entry by calling the Iterator#remove method.
public void putAll(Map<? extends K, ? extends V> m)
This method is also very simple to traverse the incoming map and just call the put method to save it.
public void clear()
Call the entrySet method to get the Set collection and then call the Set#clear() method to clear it.
public Set<K> keySet()
Returns the Set collection of Map key values. AbstractMap defines a member variable "transient Set<K> keySet". In JDK7, the keySet variable is modified by volatile, but in JDK8, it is not modified by volatile. It is explained in the comments on the keySet variable that the method to access these fields is not synchronized by itself, and volatile cannot guarantee thread safety. The implementation of the keySet method is interesting.
First of all, think about this method is to return the Set set of key values. Naturally, we can think of a simple implementation method, traverse the Entry array and take out the key value and put it in the Set set, similar to the following code:
public Set<K> keySet() { Set<K> ks = null; for (Map.Entry<K, V> entry : entrySet()) { ks.add(entry.getKey()); } return ks;}This means that every time the keySet method is called, it will traverse the Entry array, and the efficiency will be greatly reduced when the data volume is large. I have to say that the JDK source code is very well written, and it does not adopt the method of traversal. If you don't traverse Entry, how do you know that Map has added a key-value key-value pair at this time?
The answer is to re-implement a new custom Set collection inside the keySet method, and the iterator method is rewritten in this custom Set collection. Here is the key. The iterator method returns the Iterator interface, and here it is re-implemented. By calling the entrySet method and then calling its iterator method. The following is analysed in combination with the code:
public Set<K> keySet() { Set<K> ks = keySet; //Defined transient Set<K> keySet if (ks == null) { //The first call is definitely null, create a Set example through the following code ks = new AbstractSet<K>() { //Create a custom Set public Iterator<K> iterator() { //Rewrite the iterator method of the Set collection return new Iterator<K>() { //Reimplement the Iterator interface private Iterator<Entry<K,V>> i = entrySet().iterator(); //Create a Set collection Iterator iterator public boolean hasNext() { return i.hasNext(); //The judgment of the key value is the judgment of entry} public K next() { return i.next().getKey(); //The next key value is to take entry#getKey } public void remove() { i.remove(); //Delete the key value, delete entry } }; } public int size() { //The rewritten Set#size method returns AbstractMap.this.size(); //The value of the key is how big the entire map is, so just call the size method of this class. This is an internal class. Use this keyword to represent this class directly. It should indicate that the size method in AbstractMap is called. Without this, it means that it is a static static method} public boolean isEmpty() { //The rewritten Set#isEmpty method returns AbstractMap.this.isEmpty(); //For whether there is a key value, it means whether the Map is empty, so it is just to call the isEmpty method of this class} public void clear() { //The rewritten Set#clear method AbstractMap.this.clear(); //Clear the key value, it is just to clear the Map, so it is just to call the clear method of this class} public boolean contains(Object k) { //Rewrite Set#contains method return AbstractMap.this.containsKey(k); //Judge whether Set contains data k, which means whether the Map contains key value, so just call the containsKey method of this class} }; keySet = ks; // Assign this custom Set collection to the variable keySet. When calling the keySet method again in the future, because the keySet is not null, you just need to return directly. } return ks;I think this is a very clever implementation. Although this method revolves around the key value, it can actually be implemented in combination with Entry without traversing the Entry. At the same time, it is mentioned above that calling the entrySet# iterator method, which is the best practice of template method mode. Because entrySet is not implemented in AbstractMap, but is left to its subclass to complete, but the keySet method can be implemented with an "algorithm skeleton", which is the template method pattern.
public Collection<V> values()
For the values method, you can refer to keySet completely. The two have the same effect, so I will not repeat it here to save space.
public abstract Set<Entry<K,V>> entrySet()
An abstract method is handed over to its subclass to complete, indicating that this method is not particularly "general".
public boolean equals(Object o)
The Map stipulates that only when the key and value of each key-value pair in the Map correspond one by one will return true. In the method, first judge the simple conditions. If the references are equal, return true directly. If the parameter o is not the Map type, return false directly. If the number of the two Map is different, return false directly. Only then will iterate over the Entry array and compare whether the key and value in Entry correspond one by one. The method is simple, but this gives us an inspiration. In the conditional judgment, we should first judge the simple basic ones, and then judge the complex ones.
public int hashCode()
Rewrite the equals method of the Object class, and rewrite hashCode is also necessary. AbstractMap's implementation of hashCode is to add the hashCode values of all Map.Entry (here is SimpleEntry or SimpleImmutableEntry) to the end, and the final sum is used as the hashCode value of Map.
public String toString()
There is nothing to say about this method, it is to take out all key-value pairs and use StringBuilder to splice them.
protected Object clone() throws CloneNotSupportedException
Implement a shallow copy, because it is a shallow copy of the variable keySet and values, preventing the problems caused by the two shallow copies.