Understanding the difference between HashMap and TreeMap in Java

  • 2020-04-01 01:23:27
  • OfStack

Let's first introduce what a Map is. In the array, we index the contents of the array by subscript, while in the Map, we index the object by object. The object used for indexing is called key, and the corresponding object is called value. That's what we call a key-value pair.

A HashMap does a quick lookup of its contents with a hashcode, and all the elements in a TreeMap are in some sort of fixed order, which you should use if you want to get an ordered result (the order of the elements in a HashMap is not fixed).
HashMap is not thread safe TreeMap is not thread safe

Thread safety
In Java, thread safety is generally reflected in two aspects:
1. Multiple threads accessing the same Java instance (read and modify) will not interfere with each other, which is mainly reflected in the keyword synchronized. Examples are ArrayList and Vector, HashMap and Hashtable
The latter method is preceded by the synchronized keyword. If you are in the interator of a List object and another thread removes an element, the problem arises.

2. Each thread has its own field and is not Shared among multiple threads. It is mainly embodied in the java.lang.threadlocal class, which is not supported by Java keywords, such as static and transient.
1.AbstractMap abstract class and SortedMap interface
The AbstractMap class :(HashMap subclass AbstractMap) overrides the equals() and hashCode() methods to ensure that two equal mappings return the same hashCode. If two maps are of equal size, contain the same keys, and each key has the same value in both maps, then the two maps are equal. The hash code for the Map is the sum of the hash code for the Map elements, where each element is an implementation of the map.entry interface. Therefore, two equal mappings report the same hash code regardless of the internal order of the mapping.
SortedMap interface :(TreeMap inherits from SortedMap) it is used to keep the keys in order. The SortedMap interface provides access to the view (subset) of the image, including the two endpoints. SortedMap is treated the same as SortedSet except that the sort is the key acting on the map. The element added to the SortedMap implementation class must implement the Comparable interface, or you must provide its constructor with an implementation of the Comparator interface. The TreeMap class is the only implementation of it.

2. Two general Map implementations
HashMap: implementation based on a hash table. The key classes required to be added with HashMap explicitly define hashCode() and equals()[which can be overridden hashCode() and equals()], and you can tune the initial capacity and load factor to optimize the use of the HashMap space.
(1)HashMap(): build an empty hash image
(2)HashMap(Map m): build a hash image and add all mappings of image m
(3)HashMap(int initialCapacity): builds an empty hash image with a specific capacity
(4)HashMap(int initialCapacity, float loadFactor): builds an empty hash image with a specific capacity and loadFactor
TreeMap: implementation based on red-black trees. A TreeMap does not have tuning preferences because the tree is always in equilibrium.
(1)TreeMap(): build an empty image tree
(2)TreeMap(Map m): build an image tree and add all elements in image m
(3)TreeMap(Comparator c): build an image tree and use a specific Comparator to sort the keywords
(4)TreeMap(SortedMap s): build an image tree, add all the maps in the image tree s, and use the same comparator sort as the ordered image s

3. Two general Map capabilities
HashMap: for inserting, deleting, and locating elements in a Map.
Treemap: for walking keys in natural or custom order.

4. To summarize
HashMap is generally faster than TreeMap (the data structure of trees and hash tables makes this possible), and it is recommended that HashMap be used more often, with TreeMap only for maps that need to be sorted.

 
import java.util.HashMap; 
import java.util.Hashtable; 
import java.util.Iterator; 
import java.util.Map; 
import java.util.TreeMap; 
public class HashMaps { 
public static void main(String[] args) { 
Map<String, String> map = new HashMap<String, String>(); 
map.put("a", "aaa"); 
map.put("b", "bbb"); 
map.put("c", "ccc"); 
map.put("d", "ddd"); 
Iterator<String> iterator = map.keySet().iterator(); 
while (iterator.hasNext()) { 
Object key = iterator.next(); 
System.out.println("map.get(key) is :" + map.get(key)); 
} 
//Define a HashTable for testing
Hashtable<String, String> tab = new Hashtable<String, String>(); 
tab.put("a", "aaa"); 
tab.put("b", "bbb"); 
tab.put("c", "ccc"); 
tab.put("d", "ddd"); 
Iterator<String> iterator_1 = tab.keySet().iterator(); 
while (iterator_1.hasNext()) { 
Object key = iterator_1.next(); 
System.out.println("tab.get(key) is :" + tab.get(key)); 
} 
TreeMap<String, String> tmp = new TreeMap<String, String>(); 
tmp.put("a", "aaa"); 
tmp.put("b", "bbb"); 
tmp.put("c", "ccc"); 
tmp.put("d", "cdc"); 
Iterator<String> iterator_2 = tmp.keySet().iterator(); 
while (iterator_2.hasNext()) { 
Object key = iterator_2.next(); 
System.out.println("tmp.get(key) is :" + tmp.get(key)); 
} 
} 
} 

The operation results are as follows:
The map. The get (key) is: DDD
The map. The get (key) is: BBB
The map. The get (key) is: CCC
The map. The get (key) is: aaa
TAB. Get (key) is: BBB
TAB. Get (key) is: aaa
TAB. Get (key) is: DDD
TAB. Get (key) is: CCC
TMP. Get (key) is: aaa
TMP. Get (key) is: BBB
TMP. Get (key) is: CCC
TMP. Get (key) is: the CDC
The result of the HashMap is unsorted, while the result of the TreeMap output is sorted.
Now comes the topic of this article. Here's an example of how to use a HashMap:
 
import java.util.*; 
public class Exp1 { 
public static void main(String[] args){ 
HashMap h1=new HashMap(); 
Random r1=new Random(); 
for (int i=0;i<1000;i++){ 
Integer t=new Integer(r1.nextInt(20)); 
if (h1.containsKey(t)) 
((Ctime)h1.get(t)).count++; 
else 
h1.put(t, new Ctime()); 
} 
System.out.println(h1); 
} 
} 
class Ctime{ 
int count=1; 
public String toString(){ 
return Integer.toString(count); 
} 
} 

Get () to get value in a HashMap, put() to insert value, and ContainsKey() to verify that the object already exists. As you can see, a HashMap is not much different from an ArrayList operation except that it indexes its contents by key.
As mentioned earlier, HashMap is based on HashCode, and there is a HashCode() method in the superclass Object of all objects, but like the equals method, it is not applicable to all cases, so we need to override our HashCode() method. Here's an example:
 
import java.util.*; 
public class Exp2 { 
public static void main(String[] args){ 
HashMap h2=new HashMap(); 
for (int i=0;i<10;i++) 
h2.put(new Element(i), new Figureout()); 
System.out.println("h2:"); 
System.out.println("Get the result for Element:"); 
Element test=new Element(5); 
if (h2.containsKey(test)) 
System.out.println((Figureout)h2.get(test)); 
else 
System.out.println("Not found"); 
} 
} 
class Element{ 
int number; 
public Element(int n){ 
number=n; 
} 
} 
class Figureout{ 
Random r=new Random(); 
boolean possible=r.nextDouble()>0.5; 
public String toString(){ 
if (possible) 
return "OK!"; 
else 
return "Impossible!"; 
} 
} 

In this example, Element is used to index the object Figureout, i.e., Element is key and Figureout is value. Generate a random floating point number in Figureout and print "OK!" if it is larger than 0.5. "Impossible!" . Then check the corresponding Figureout result of Element(3).

It turns out that no matter how many times you run it, the result is "Not found." That is, the index Element(3) is not in the HashMap. How is that possible?
The reason is slow: Element's HashCode method inherits from Object, and the HashCode method in Object returns a HashCode corresponding to the current address, meaning that HashCode () returns different values for different objects, even if their contents are identical. This actually goes against our intentions. Because when we use a HashMap, we want to get the same target object using an object index of the same content, which requires that HashCode() return the same value at this point. In the example above, we expect newElement(I) (I =5) to be the same as Elementtest=newElement(5), when in fact these are two different objects, and although they have the same content, they have different addresses in memory. So it's natural that the program above won't get the results we're looking for. The following changes are made to the Element class:
 
class Element{ 
int number; 
public Element(int n){ 
number=n; 
} 
public int hashCode(){ 
return number; 
} 
public boolean equals(Object o){ 
return (o instanceof Element) && (number==((Element)o).number); 
} 
} 

Here Element overrides the hashCode() and equals() methods in Object. Overwrite hashCode() so that it returns the value of number as hashCode, so that objects with the same content have the same hashCode. Overriding equals() makes sense when a HashMap determines whether two keys are equal (see my other article, rewriting methods in the Object class). The modified program runs as follows:
H2:
Get the result for Element:
Impossible!
Remember: if you want to effectively use a HashMap, you must override its HashCode().
There are two other rules for overriding HashCode() :
[the list = 1]
You don't have to generate a unique hashcode for every different object, as long as your hashcode method enables get() to get what put() is put into. That is, "not a rule."

The algorithm that generates hashcode tries to scatter the values of hashcode as much as possible, rather than concentrating many hashcodes into a single range, which helps improve the performance of the HashMap. The principle of dispersion. As for the specific reasons for the second principle, those interested can refer to Bruce Eckel's Thinking in Java, where there is an introduction to the internal implementation principles of HashMap, which is not detailed here.
With these two principles in hand, you can write your own programs using HashMap. Notice that the three methods provided in java.lang.object, clone(), equals(), and hashCode(), are typical, but do not work in many cases, simply by looking at the address of the Object. This requires us to rewrite them in our own programs, and there are literally thousands of such methods in the Java class library. Using object-oriented polymorphism - coverage, the Java designers elegantly built the structure of Java, which further demonstrates the fact that Java is a pure OOP language.


Related articles: