基于hash算法的三个集合HashTable,HashSet和HashMap详解

位运算符

下表列出了位运算符的基本运算,假设整数变量 A 的值为 60 和变量 B 的值为 13:

public class Test {
  public static void main(String[] args) {
     int a = 60; /* 60 = 0011 1100 */ 
     int b = 13; /* 13 = 0000 1101 */
     int c = 0;
     c = a & b;       /* 12 = 0000 1100 */
     System.out.println("a & b = " + c );
 
     c = a | b;       /* 61 = 0011 1101 */
     System.out.println("a | b = " + c );
 
     c = a ^ b;       /* 49 = 0011 0001 */
     System.out.println("a ^ b = " + c );
 
     c = ~a;          /*-61 = 1100 0011 */
     System.out.println("~a = " + c );
 
     c = a << 2;     /* 240 = 1111 0000 */
     System.out.println("a << 2 = " + c );
 
     c = a >> 2;     /* 15 = 1111 */
     System.out.println("a >> 2  = " + c );
  
     c = a >>> 2;     /* 15 = 0000 1111 */
     System.out.println("a >>> 2 = " + c );
  }
} 

Hashtable和HashMap  

      区别一:继承的父类不同


  Hashtable 继承自 Dictionary 类,而 HashMap 继承自AbstractMap 类。但二者都实现了 Map 接口。


  区别二:线程安全性不同


  Hashtable 中的方法是 Synchronize 的,而 HashMap 中的方法在缺省情况下是非 Synchronize 的。


  区别三:是否提供 contains 方法


  HashMap 把 Hashtable 的 contains 方法去掉了,改成containsValue 和 containsKey,因为 contains 方法容易让人引起误解。
  Hashtable 则保留了 contains,containsValue 和 containsKey三个方法,其中 contains 和 containsValue 功能相同。


  区别四:**key 和 value 是否允许 null 值 (面试比较喜欢问)


  其中 key 和 value 都是对象,并且不能包含重复 key,但可以包含重复的 value。


  Hashtable 中,key 和 value 都不允许出现 null 值。


  HashMap 中,null 可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为 null。当 get()方法返回 null值时,可能是 HashMap 中没有该键,也可能使该键所对应的值为 null。因此,在 HashMap 中不能由 get()方法来判断 HashMap 中是否存在某个键, 而应该用 containsKey()方法来判断。


  区别五:哈希值的计算方法不同

       Hashtable 直接使用的是对象的 hashCode,而 HashMap 则是在对象的 hashCode 的基础上还进行了一些变化。

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

  区别六:内部实现使用的数组初始化和扩容方式不同


  内存初始大小不同,HashTable 初始大小是 11,而 HashMap 初始大小是 16

HashMap和HashSet区别

//HashSet底层用来存储元素的结构,实际上使用HashMap来存储
private transient HashMap<E,Object> map;
 
//HashMap中的value值,HashSet只关注key值,所以所有的value值都为Object对象
private static final Object PRESENT = new Object();
 
 
//HashSet的无参构造,直接创建了一个HashMap对象
public HashSet() {
        map = new HashMap<>();
}
 
//指定初始化容量和负载因子
public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
}
 
//给定初始化容量
public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
}
 
public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
}

可以看到 HashSet的构造方法底层都是调用 HashMap的构造方法, 所以HashSet底层实际上是使用 HashMap 来作为存储结构.

当使用无参构造创建 HashSet对象时, 其实调用了 HashMap的无参构造创建了一个 HashMap对象, 所以 HashSet 的初始化容量也为16, 负载因子也为 0.75.

再来看看 HashSet 的 add() 方法的实现:

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

可以看到 HashSet 的 add() 方法底层实际也是调用了 HashMap 的 put() 方法, 这里的key为我们传入的将要添加到 set集合中的元素, 而value值则为 PERSENT,其实就是上面分析的 HashSet类中的一个静态字段, 默认为 Object对象.

HashSet并不关注value元素, 只使用 HashMap来存储 key元素, 这就使得 HashSet判断元素相等的条件与 HashMap中 key相等的条件其实是一样的, 两个元素的 hashCode值相同且通过equals()方法比较返回 true.

所以HashSet应该重写 equals()和hashCode()方法, 两个元素的 HashCode相同, 保证通过equals() 方法比较返回 true.

总结一下HashSet和HashMap的区别:
(1)HashSet实现了Set接口, 仅存储对象; HashMap实现了 Map接口, 存储的是键值对.

(2)HashSet底层其实是用HashMap实现存储的, HashSet封装了一系列HashMap的方法. 依靠HashMap来存储元素值,(利用hashMap的key键进行存储), 而value值默认为Object对象. 所以HashSet也不允许出现重复值, 判断标准和HashMap判断标准相同, 两个元素的hashCode相等并且通过equals()方法返回true.

posted @ 2022-04-13 09:17  勤奋的园  阅读(110)  评论(0编辑  收藏  举报