那些你不知道的数据结构之map

HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap
If you want to post code and let me or someone else review it, please format your code in eclipse and put the code inside <pre> and </pre> tags. 
  • Martin Carney

    Your Dog.equals() and Dog.hashCode() methods are CRAP.

    The equals method uses == to compare two Strings. If you need to check that two Strings are the same, use String.equals(), not ==. The == operator checks if the two objects have the same memory address, that is, if they are in fact the same object, as opposed to two identical objects. The ONLY reason you example works is because Java only creates one instance of each hardcoded String in the source when the program starts up.

    The equals method also does not check to make sure the object being compared to is a Dog object. If a Cat is passed in, you will get an exception for casting a Cat as a Dog.

    The hashCode method is not robust in the slightest. Instead of returning the string’s length, which has a high probability of repeating and clusters in the range of 1-10, you should at least do something like “return color.hashCode()” and take advantage of String’s robust hashCode method.

    The point of a hashCode method is to generate a reproducible value based on the object’s attributes, such that any two objects which are o1.equals(o2) == true will produce the same hash, but any two objects which are not equal are unlikely to produce the same hash. The hashes should be fairly evenly distributed between Integer.MAX_VALUE and Integer.MIN_VALUE, or at least 0 and Integer.MAX_VALUE. If you don’t know how to generate a good hash algorithm, then just leverage String’s hashCode() function on whatever String or combination of Strings is most likely to be unique between two different objects of that type.

  • Jake Locker

    There is concurrentHashMap but which was added a while back. That being said I believe only one thread at a time can alter a concurrent hash map and it is still not as predictable as HashTable.

  • Jake Locker

    Also, HashMap is better solution if you are only using a single thread and is more scalable. Not sure if that is to much detail for this brief summary though.

  • OFS

    Hashtable class methods all are Synchronized.

    HashMap class methods are not Synchronized. 

    for example you created a method with Synchronized, in that you wrote some code using HashMap Or you added some elements to HashMap and displaying using keys in that method. So no two threads cannot access the method at a time. if the same method is not Synchronized, multiple threads can access the method then HashMap code will be broken.

    if you use HashTable instead of HashMap in non synchronized method the code wont break bcoz HashTable already have synchronized methods they wont allow two threads to access.

    thread safe means only one thread can allow. 

    putting HashTable inside thread safe method is no use, so put HashMap inside thread safe

    pleas correct me if it is wrong.

  • Daniel Gurianov

    Because 40 is weight inside the key, by which  key-value pairs  are sorted (due to overriden compareTo method). 5 in the output is the value , that is extracted by entry.getValue().
    To simplify , here how looks element of treeMap here  [ KEY :  DogObject(FieldColor=white, fieldSize=40)  : VALUE: 5 ]
    Code takes KEY  that is DogObject, compareTo takes fieldSize and compare with others same fieldSizes in  other DogObjects that already present in KEYs, then place  key-pair  DogObject : 5  to some pleace of the map , depending of  is 40 is big or not.

  • http://www.pcclm.com/               Piyush Chordia

    Thanks. good stuff

  • Vikram Kakkar

    A very clear explanation. Thanks a lot.

  • Juan Carlos Alvarez

    I share the same doubt

  • Michael Tirkey

    Awesome explanation .. really cleared my doubts. Thank for the great post.

    I had one doubt though..For tree map;

    If “Dog d4 = new Dog(“white”, 10);” is replaced with “Dog d4 = new Dog(“white”, 40);”, the output would be:

    white dog – 20
    red dog – 10
    black dog – 15
    white dog – 5

    Why will the output for white dog not be 40 ? and why is output as 5 ?

  • sreenath

    HI, Hashtable is thread safe , but you mentioned as below,  hashmap is thread safe please correct it…

    “This gives us the reason that HashMap should be used if it is thread-safe, since Hashtable has overhead for synchronization.”

  • Seung Hyun Han

    The best article I have ever seen!!

  • Aman Mahajan

    Thanks so much!

  • Sunny

    Nice tutorial. All at one place.

  • MengWoo

    Great article and website, hope to see more coming,

  • Yolo

    Great Article!

  • Akshay Johri

    great explanation. Thanks!

  • Julia Soldatenko

    That is the best explanation I’ve read so far! Thanks a lot!

  • http://www.fromdev.com/               Java Developer

    Excellent and concise explanation. Thanks for the tutorial it helped me quickly decide.

 

 

In general, Map is a data structure consisting of a set of key-value pairs, and each key can only appears once in the map. This post summarizes Top 9 FAQ of how to use Java Map and its implemented classes. For sake of simplicity, I will use generics in examples. Therefore, I will just write Map instead of specific Map. But you can always assume that both the K and V are comparable, which means K extends Comparable and V extends Comparable.


0. Convert a Map to List

In Java, Map interface provides three collection views: key set, value set, and key-value set. All of them can be converted to List by using a constructor or addAll() method. The following snippet of code shows how to construct an ArrayList from a map.

// key list
List keyList = new ArrayList(map.keySet());
// value list
List valueList = new ArrayList(map.valueSet());
// key-value list
List entryList = new ArrayList(map.entrySet());

1. Iterate over each Entry in a Map

Iterating over every pair of key-value is the most basic operation to traverse a map. In Java, such pair is stored in the map entry called Map.Entry. Map.entrySet() returns a key-value set, therefore the most efficient way of going through every entry of a map is

for(Entry entry: map.entrySet()) {
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

Iterator can also be used, especially before JDK 1.5

Iterator itr = map.entrySet().iterator();
while(itr.hasNext()) {
  Entry entry = itr.next();
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

2. Sort a Map on the keys

Sorting a map on the keys is another frequent operation. One way is to put Map.Entry into a list, and sort it using a comparator that sorts the value.

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {
 
  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getKey().compareTo(e2.getKey());
  }
 
});

The other way is to use SortedMap, which further provides a total ordering on its keys. Therefore all keys must either implement Comparable or be accepted by the comparator.

One implementing class of SortedMap is TreeMap. Its constructor can accept a comparator. The following code shows how to transform a general map to a sorted map.

SortedMap sortedMap = new TreeMap(new Comparator() {
 
  @Override
  public int compare(K k1, K k2) {
    return k1.compareTo(k2);
  }
 
});
sortedMap.putAll(map);

3. Sort a Map on the values

Putting the map into a list and sorting it works on this case too, but we need to compare Entry.getValue() this time. The code below is almost same as before.

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {
 
  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
  }
 
});

We can still use a sorted map for this question, but only if the values are unique too. Under such condition, you can reverse the key=value pair to value=key. This solution has very strong limitation therefore is not really recommended by me.

4. Initialize a static/immutable Map

When you expect a map to remain constant, it's a good practice to copy it into an immutable map. Such defensive programming techniques will help you create not only safe for use but also safe for thread maps.

To initialize a static/immutable map, we can use a static initializer (like below). The problem of this code is that, although map is declared as static final, we can still operate it after initialization, like Test.map.put(3,"three");. Therefore it is not really immutable. To create an immutable map using a static initializer, we need an extra anonymous class and copy it into a unmodifiable map at the last step of initialization. Please see the second piece of code. Then, an UnsupportedOperationException will be thrown if you run Test.map.put(3,"three");.

public class Test {
 
  private static final Map map;
  static {
    map = new HashMap();
    map.put(1, "one");
    map.put(2, "two");
  }
}
public class Test {
 
  private static final Map map;
  static {
    Map aMap = new HashMap();
    aMap.put(1, "one");
    aMap.put(2, "two");
    map = Collections.unmodifiableMap(aMap);
  }
}

Guava libraries also support different ways of intilizaing a static and immutable collection. To learn more about the benefits of Guava's immutable collection utilities, see Immutable Collections Explained in Guava User Guide.

5. Difference between HashMap, TreeMap, and Hashtable

There are three main implementations of Map interface in Java: HashMap, TreeMap, and Hashtable. The most important differences include:

  1. The order of iteration. HashMap and Hashtable make no guarantees as to the order of the map; in particular, they do not guarantee that the order will remain constant over time. But TreeMap will iterate the whole entries according the "natural ordering" of the keys or by a comparator.
  2. key-value permission. HashMap allows null key and null values (Only one null key is allowed because no two keys are allowed the same). Hashtable does not allow null key or null values. If TreeMap uses natural ordering or its comparator does not allow null keys, an exception will be thrown.
  3. Synchronized. Only Hashtable is synchronized, others are not. Therefore, "if a thread-safe implementation is not needed, it is recommended to use HashMap in place of Hashtable."

A more complete comparison is

                 | HashMap | Hashtable | TreeMap
-------------------------------------------------------
iteration order  | no      | no        | yes
null key-value   | yes-yes | no-no   | no-yes
synchronized     | no      | yes       | no
time performance | O(1)    | O(1)      | O(log n)
implementation   | buckets | buckets   | red-black tree

Read more about HashMap vs. TreeMap vs. Hashtable vs. LinkedHashMap.

6. A Map with reverse view/lookup

Sometimes, we need a set of key-key pairs, which means the map's values are unique as well as keys (one-to-one map). This constraint enables to create an "inverse lookup/view" of a map. So we can lookup a key by its value. Such data structure is called bidirectional map, which unfortunetely is not supported by JDK.

Both Apache Common Collections and Guava provide implementation of bidirectional map, called BidiMap and BiMap, respectively. Both enforce the restriction that there is a 1:1 relation between keys and values.
7. Shallow copy of a Map

Most implementation of a map in java, if not all, provides a constructor of copy of another map. But the copy procedure is not synchronized. That means when one thread copies a map, another one may modify it structurally. To [prevent accidental unsynchronized copy, one should use Collections.synchronizedMap() in advance.

Map copiedMap = Collections.synchronizedMap(map);

Another interesting way of shallow copy is by using clone() method. However it is NOT even recommended by the designer of Java collection framework, Josh Bloch. In a conversation about "Copy constructor versus cloning", he said

I often provide a public clone method on concrete classes because people expect it. ... It's a shame that Cloneable is broken, but it happens. ... Cloneable is a weak spot, and I think people should be aware of its limitations.

For this reason, I will not even tell you how to use clone() method to copy a map.
8. Create an empty Map

If the map is immutable, use

map = Collections.emptyMap();

Otherwise, use whichever implementation. For example

map = new HashMap();

THE END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值