Java中HashSet,HashMap和HashTable的区别(转)

HashMap、HashSet、HashTable之间的区别是Java程序员的一个常见面试题目,在此仅以此博客记录,并深入源代码进行分析:

在分析之前,先将其区别列于下面

1:HashSet底层采用的是HashMap进行实现的,但是没有key-value,只有HashMap的key set的视图,HashSet不容许重复的对象

2:Hashtable是基于Dictionary类的,而HashMap是基于Map接口的一个实现

3:Hashtable里默认的方法是同步的,而HashMap则是非同步的,因此Hashtable是多线程安全的

4:HashMap可以将空值作为一个表的条目的key或者value,HashMap中由于键不能重复,因此只有一条记录的Key可以是空值,而value可以有多个为空,但HashTable不允许null值(键与值均不行)

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

6:内存扩容时采取的方式也不同,Hashtable采用的是2*old+1,而HashMap是2*old。

7:哈希值的计算方法不同,Hashtable直接使用的是对象的hashCode,而HashMap则是在对象的hashCode的基础上还进行了一些变化

源代码分析:

对于区别1,看下面的源码

//HashSet类的部份源代码
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{   //用于类的序列化,可以不用管它
    static final long serialVersionUID = -5024744406713321676L;
    //从这里可以看出HashSet类里面真的是采用HashMap来实现的
    private transient HashMap<E,Object> map;  

    // Dummy value to associate with an Object in the backing Map
    //这里是生成一个对象,生成这个对象的作用是将每一个键的值均关联于此对象,以满足HashMap的键值对
    private static final Object PRESENT = new Object();  

    /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    //这里是一个构造函数,开构生成一个HashMap对象,用来存放数据
    public HashSet() {
    map = new HashMap<E,Object>();
    }

  从上面的代码中得出的结论是HashSet的确是采用HashMap来实现的,而且每一个键都关键同一个Object类的对象,因此键所关联的值没有意义,真正有意义的是键。而HashMap里的键是不允许重复的,因此1也就很容易明白了。

对于区别2,继续看源代码如下

//从这里可以看得出Hashtable是继承于Dictionary,实现了Map接口
public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable {
//这里可以看出的是HashMap是继承于AbstractMap类,实现了Map接口
//因此与Hashtable继承的父类不同
public class HashMap<K,V>
    extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable

  区别3,找一个具有针对性的方法看看,这个方法就是put

//Hashtable里的向集体增加键值对的方法,从这里可以明显看到的是
//采用了synchronized关键字,这个关键字的作用就是用于线程的同步操作
//因此下面这个方法对于多线程来说是安全的,但这会影响效率
public synchronized V put(K key, V value) {
    // Make sure the value is not null
    //如果值为空的,则会抛出异常
    if (value == null) {
        throw new NullPointerException();
    }  

    // Makes sure the key is not already in the hashtable.
    Entry tab[] = table;
    //获得键值的hashCode,从这里也可以看得出key!=null,否则的话会抛出异常的呦
    int hash = key.hashCode();
    //获取键据所在的哈希表的位置
    int index = (hash & 0x7FFFFFFF) % tab.length;
    //从下面这个循环中可以看出的是,内部实现采用了链表,即桶状结构
    for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
        //如果向Hashtable中增加同一个元素时,则会重新更新元素的值
        if ((e.hash == hash) && e.key.equals(key)) {
                V old = e.value;
                e.value = value;
                return old;
        }
    }
    //后面的暂时不用管它,大概的意思就是内存的个数少于某个阀值时,进行重新分配内存
    modCount++;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();  

            tab = table;
            index = (hash & 0x7FFFFFFF) % tab.length;
    }
//HashMap中的实现则相对来说要简单的很多了,如下代码
//这里的代码中没有synchronize关键字,即可以看出,这个关键函数不是线程安全的
    public V put(K key, V value) {
    //对于键是空时,将向Map中放值一个null-value构成的键值对
    //对值却没有进行判空处理,意味着可以有多个具有键,键所对应的值却为空的元素。
        if (key == null)
            return putForNullKey(value);
    //算出键所在的哈希表的位置
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
    //同样从这里可以看得出来的是采用的是链表结构,采用的是桶状
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //对于向集体中增加具有相同键的情况时,这里可以看出,并不增加进去,而是进行更新操作
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        //开始增加元素
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

  

区别4在上面的代码中,已经分析了,可以再细看一下

区别5内存初化大小不同,看看两者的源代码:

public Hashtable() {
   //从这里可以看出,默认的初始化大小11,这里的11并不是11个字节,而是11个Entry,这个Entry是
   //实现链表的关键结构
   //这里的0.75代表的是装载因子
this(11, 0.75f);
 }
//这里均是一些定义
 public HashMap() {
 //这个默认的装载因子也是0.75
     this.loadFactor = DEFAULT_LOAD_FACTOR;
 //默认的痤为0.75*16
     threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
 //这里开始是默认的初始化大小,这里大小是16
     table = new Entry[DEFAULT_INITIAL_CAPACITY];
     init();
 }

  

从上面的代码中,可以看出的是两者的默认大小是不同的,一个是11,一个是16

区别6内存的扩容方式,看一看源代码也是很清楚的,其实区别是不大的,看到网上一哥们写的,说两者有区别,其实真正深入源码,区别真不大,一个是2*oldCapacity+1, 一个是2*oldCapacity,你说大吗:)

//Hashtable中调整内存的函数,这个函数没有synchronize关键字,但是protected呦
protected void rehash() {
    //获取原来的表大小
    int oldCapacity = table.length;
    Entry[] oldMap = table;
  //设置新的大小为2*oldCapacity+1
    int newCapacity = oldCapacity * 2 + 1;
    //开设空间
    Entry[] newMap = new Entry[newCapacity];
  //以下就不用管了。。。
    modCount++;
    threshold = (int)(newCapacity * loadFactor);
    table = newMap;  

    for (int i = oldCapacity ; i-- > 0 ;) {
        for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
        Entry<K,V> e = old;
        old = old.next;  

        int index = (e.hash & 0x7FFFFFFF) % newCapacity;
        e.next = newMap[index];
        newMap[index] = e;
        }
    }
    }
//HashMap中要简单的多了,看看就知道了
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry<K,V> e = table[bucketIndex];
       table[bucketIndex] = new Entry<K,V>(hash, key, value, e);
       //如果超过了阀值
       if (size++ >= threshold)
       //就将大小设置为原来的2倍
           resize(2 * table.length);
   }

对于区别7的哈希值计算方法的不同,源码面前,同样是了无秘密

//Hashtable中可以看出的是直接采用关键字的hashcode作为哈希值
int hash = key.hashCode();
//然后进行模运算,求出所在哗然表的位置
int index = (hash & 0x7FFFFFFF) % tab.length;
//HashMap中的实现
//这两行代码的意思是先计算hashcode,然后再求其在哈希表的相应位置
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);

上面的HashMap中可以看出关键在两个函数hash与indexFor

源码如下:

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    //这个我就不多说了,>>>这个是无符号右移运算符,可以理解为无符号整型
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
//求位于哈希表中的位置
 static int indexFor(int h, int length) {
     return h & (length-1);
 }

出处链接:http://www.cnblogs.com/ckwblogs/p/6019419.html

时间: 2024-12-26 00:10:41

Java中HashSet,HashMap和HashTable的区别(转)的相关文章

java面试题——HashMap和Hashtable 的区别

一.HashMap 和Hashtable 的区别 我们先看2个类的定义 1 public class Hashtable 2 extends Dictionary 3 implements Map, Cloneable, java.io.Serializable public class HashMap extends AbstractMap implements Map, Cloneable, Serializable 可见Hashtable 继承自 Dictiionary 而 HashMap

Java中的HashMap和HashTable到底哪不同?

学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:456544752  我们一起学Java! HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的HashMap和HashTable基于JDK 1.7.0_67.源码见这里 1. 时间 HashTable产生于JDK 1.1,而HashMap产生

Java中的HashMap和HashTable到底哪不同?(原文参考来自码农网)

HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的HashMap和HashTable基于JDK 1.7.0_67.源码见这里 1. 时间 HashTable产生于JDK 1.1,而HashMap产生于JDK 1.2.从时间的维度上来看,HashMap要比HashTable出现得晚一些. 2. 作者 以下是HashTable的作者: 以下代码及注释来自ja

Java中的HashMap和HashTable

对于 Map ,最直观就是理解就是键值对,映射,key-value 形式.一个映射不能包含重复的键,一个键只能有一个值.平常我们使用的时候,最常用的无非就是 HashMap. HashMap 实现了 Map 接口,允许使用 null 值 和 null 键,并且不保证映射顺序. HashMap 有两个参数影响性能: 初始容量:表示哈希表在其容量自动增加之前可以达到多满的一种尺度加载因子:当哈希表中的条目超过了容量和加载因子的乘积的时候,就会进行重哈希操作.如下成员变量源码: 1 static fi

Java 中的 ==, equals 与 hashCode 的区别与联系

一.概述 1.概念 == : 该操作符生成的是一个boolean结果,它计算的是操作数的值之间的关系 equals : Object 的 实例方法,比较两个对象的content是否相同 hashCode : Object 的 native方法 , 获取对象的哈希值,用于确定该对象在哈希表中的索引位置,它实际上是一个int型整数 二.关系操作符 == 1.操作数的值 基本数据类型变量 在Java中有八种基本数据类型: 浮点型:float(4 byte), double(8 byte) 整型:byt

[转]HashMap与HashTable的区别、HashMap与HashSet的关系

转自: http://blog.csdn.net/wl_ldy/article/details/5941770 HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable.可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别. 一:HashMap与HashTable的区别 1.HashTable的方法是同步 的,在方法的前面都有synchronized来同步,HashMap未经同步,所以

HashMap与HashTable的区别、HashMap与HashSet的关系

http://blog.csdn.net/wl_ldy/article/details/5941770 HashTable的应用非常广泛,HashMap是新框架中用来代替HashTable的类,也就是说建议使用HashMap,不要使用HashTable.可能你觉得HashTable很好用,为什么不用呢?这里简单分析他们的区别. 一:HashMap与HashTable的区别 1.HashTable的方法是同步 的,在方法的前面都有synchronized来同步,HashMap未经同步,所以在多线程

从Java源码的角度来分析HashMap与HashTable的区别

由于HashMap与HashTable都是用来存储Key-Value的键值对,所以经常拿来对比二者的区别,下面就从源码的角度来分析一下HashMap与HashTable的区别, 首先介绍一下两者的区别,然后再从源码分析. HahMap与HahTable两者主要区别: 1.继承的父类不同 <span style="font-size:18px;">public class HashMap<K, V> extends AbstractMap<K, V>

Java 集合系列 11 hashmap 和 hashtable 的区别

java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java 集合系列 04 LinkedList详细介绍(源码解析)和使用示例 Java 集合系列 05 Vector详细介绍(源码解析)和使用示例 Java 集合系列 06 Stack详细介绍(源码解析)和使用示例 Java 集合系列 07 List总结(LinkedList, ArrayList等使用场景和

java 的HashMap和HashTable的区别?

1.HashMap和HashTable的区别? HashMap不是线程安全的它的操作方法没有进行同步处理 HashMap允许key为空 HashTable是线程安全的它的操作方法加以了synchronized修饰 HashTable不允许Key为空 2.ArrayList和LinkedList和Vector的区别? ArrayList内部实现是一个object数组,非线程安全 LinkedList内部实现是一个链表结构,非线程安全 Vector内部实现是一个object数组,线程安全 原文地址: