Hashtable、HashMap和TreeMap有什么区别?

典型的回答

Hashtable、HashMap、TreeMap都是最常见的Map接口的实现,是以键值对的形式存储和操作数据的容器类型。

Hashtable是早期Java类库提供的一个哈希表实现,本身是线程安全的,不支持null键和值。由于线程安全导致的性能开销,所以已经很少被推荐使用。

HashMap是应用更加广泛的哈希表实现,行为上大致与Hashtable一致,主要区别在于HashMap不是线程安全的,且支持null键和值等。通常情况下,HashMap进行put或者get操作,可以达到常数时间的性能,所以它是绝大部分利用键值对存取场景的首选

TreeMap则是基于红黑树的一种提供顺序访问的Map,和HashMap不同,它的get、put、remove之类操作都是O(log(n))的时间复杂度,具体顺序可以由指定的Comparator来决定,或者根据键的自然顺序来判断。

知识扩展

1、Map整体结构

Map被包括在Java集合框架里,但其本身并不是狭义上的集合类型(Collection)。

Hashtable比较特别,作为类似Vector、Stack的早期集合相关类型,它是扩展了Dictionary类,结构上与HashMap之类明显不同。

大部分使用Map的场景,通常就是放入、访问或者删除,而对顺序没有特别要求。HashMap在这种情况下基本上是最好的选择。HashMap的性能表现非常依赖于哈希码的有效性,请务必掌握hashCode和equals的一些基本约定:

  • equals相等,hashCode一定要相等。
  • 重写了hashCode也要重写equals。
  • hashCode需要保持一致性,状态改变返回的哈希值仍然要一致。
  • equals要满足对称、反射与传递性。

还有部分使用Map的场景中,对于键值对的顺序是有要求的。这时就应该考虑使用LinkedHashMap和TreeMap。

2、LinkedHashMap vs. TreeMap

虽然LinkedHashMap和TreeMap都可以保证某种顺序,但二者还是非常不同的。

TreeMap是利用红黑树来实现的。树中的每个节点的值,都会大于或等于它的左子树中的所有节点的值,并且小于或等于它的右子树中的所有节点的值。它实现了SortedMap接口,能够对保存的记录根据键进行排序。也就是说TreeMap的整体顺序是由键的顺序关系决定的,通过Comparator或Comparable(自然顺序)来决定。类似于hashCode和equals的约定,为了避免模棱两可的情况,自然顺序同样需要符合一个约定:compareTo的返回值需要和equals一致。

LinkedHashMap通常提供的是遍历顺序符合插入顺序。它的实现是通过为条目(键值对)维护一个双向链表。注意,通过特定的构造函数,我们可以创建反映访问顺序的实例,所谓的put、get、compute等,都算作“访问”。

这种行为适用于一些特定应用场景,例如,我们构建一个空间占用敏感的资源池,希望可以自动将最不常被访问的对象释放掉,这就可以利用LinkedHashMap提供的机制来实现。参考以下示例:

public class LinkedHashMapSample {
  public static void main(String[] args) {
    LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(16, 0.75f, true) {
      @Override
      protected boolean removeEldestEntry(Map.Entry eldest) {
        // 实现自定义的删除策略,否则行为就和普通Map没有区别。
        return size() > 3;
      }
    };
    map.put("Project1", "Apple");
    map.put("Project2", "Banana");
    map.put("Project3", "Cherry");
    map.forEach((k, v) -> {
      System.out.println(k + ":" + v);
    });
    // 模拟访问
    map.get("Project2");
    map.get("Project2");
    map.get("Project3");
    System.out.println();
    map.forEach((k, v) -> {
      System.out.println(k + ":" + v);
    });
    // 触发删除
    map.put("Project4", "Mango");
    System.out.println();
    map.forEach((k, v) -> {
      System.out.println(k + ":" + v);
    });
  }
}

输出如下:

Project1:Apple
Project2:Banana
Project3:Cherry

Project1:Apple
Project2:Banana
Project3:Cherry

Project2:Banana
Project3:Cherry
Project4:Mango

猜你喜欢

转载自blog.csdn.net/qweqwruio/article/details/81349299