`
kill2you
  • 浏览: 5053 次
  • 性别: Icon_minigender_1
  • 来自: 上海
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

从源码看hashmap与hashtable区别

阅读更多
先看看hashmap与hashtable中的数据结构
 static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;//存储冲突中链表下一个元素
        final int hash;//依靠hash来索引map

从构造器看看两者不同
hashmap默认初始化容量为16;当使用非默认构造器时,其初始容量并非为所设置的容量initialCapacity,而是使用capacity,这里对初始容量做了个优化,保证初始容量为2的倍数,可以自己研究下原因。
还有在使用构造器初始化的时候最终hashmap都会调到init();
 /**
     * Initialization hook for subclasses. This method is called
     * in all constructors and pseudo-constructors (clone, readObject)
     * after HashMap has been initialized but before any entries have
     * been inserted.  (In the absence of this method, readObject would
     * require explicit knowledge of subclasses.)
     */
    void init() {
    }

  public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR;
        threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
        table = new Entry[DEFAULT_INITIAL_CAPACITY];
        init();
    }

public HashMap(int initialCapacity, float loadFactor) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);

        // Find a power of 2 >= initialCapacity
        int capacity = 1;
       //看看这里所使用的初始容量
       while (capacity < initialCapacity)
            capacity <<= 1;       
        this.loadFactor = loadFactor;
        threshold = (int)(capacity * loadFactor);
        table = new Entry[capacity];
        init();
    }

hashtable默认初始化容量为11,并且需要调用Hashtable(int initialCapacity, float loadFactor);
    public Hashtable() {
	this(11, 0.75f);
    }
 public Hashtable(int initialCapacity, float loadFactor) {
	if (initialCapacity < 0)
	    throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal Load: "+loadFactor);

        if (initialCapacity==0)
            initialCapacity = 1;
	this.loadFactor = loadFactor;
	table = new Entry[initialCapacity];
	threshold = (int)(initialCapacity * loadFactor);
    }


看完初始化我们看下起最主要的不同方法PUT;
hashmap中,支持key和value都为空的情况,使用的hash为重新计算后的hash值,使用计算位置的方式也不一样 h & (length-1);当hashmap中的容量超过临界值时是将容量扩大为原来的1倍2 * table.length;
hashtable(同步)中当key为空的时候就直接报空指针,hash使用的是类自身的hashcode方法,计算位置使用的(hash & 0x7FFFFFFF) % tab.length;当hashtable中的容量超过临界值时是将容量扩大为oldCapacity * 2 + 1;

还可以看下两者在重新分配空间时候的做法。
 public V put(K key, V value) {
    	//处理key为空的特殊情况
        if (key == null)
            return putForNullKey(value);
        //使用key的hashcode再hash
        int hash = hash(key.hashCode());
        //i为放在hashmap中的位置
        int i = indexFor(hash, table.length);
        //当相同位置有多个值时候,循环查找。
        for (Entry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            //如果key为一样则返回原来的旧值,把新值插入到原key中
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        //把对应的新map添加到i位置中
        addEntry(hash, key, value, i);
        return null;
    }

static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }
 static int indexFor(int h, int length) {
        return h & (length-1);
    }

 void addEntry(int hash, K key, V value, int bucketIndex) {
	Entry<K,V> e = table[bucketIndex];
	//把每个冲突节点的旧链表放入Entry的next中
        table[bucketIndex] = new Entry<K,V>(hash, key, value, e);        
        if (size++ >= threshold)
        	//如果map中内容超出threshold则将容量扩大1倍
            resize(2 * table.length);
    }

 void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        //构造新的map空间
        Entry[] newTable = new Entry[newCapacity];
        //将原有map中内容重新hash到新的位置
        transfer(newTable);
        table = newTable;
        threshold = (int)(newCapacity * loadFactor);
    }
 void transfer(Entry[] newTable) {
        Entry[] src = table;
        int newCapacity = newTable.length;
        for (int j = 0; j < src.length; j++) {
            Entry<K,V> e = src[j];
            if (e != null) {
            	//释放原有table在内存中的位置
                src[j] = null;
                //如果当前节点里面有多个值,需要对每个值重新分配位置
                do {
                    Entry<K,V> next = e.next;
                    int i = indexFor(e.hash, newCapacity);
                    e.next = newTable[i];
                    newTable[i] = e;
                    e = next;
                } while (e != null);
            }
        }
    }




 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;
	int hash = key.hashCode();
	int index = (hash & 0x7FFFFFFF) % tab.length;
	for (Entry<K,V> e = tab[index] ; e != null ; e = e.next) {
	    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;
	}

	// Creates the new entry.
	Entry<K,V> e = tab[index];
	tab[index] = new Entry<K,V>(hash, key, value, e);
	count++;
	return null;
    }
 protected void rehash() {
	int oldCapacity = table.length;
	Entry[] oldMap = table;

	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;
	    }
	}
    }


其他都有的方法基本类似

参考
http://blog.csdn.net/romans1981/archive/2005/03/07/313232.aspx
http://tieba.baidu.com/f?kz=890094718








分享到:
评论

相关推荐

    HashMap与HashTable的区别(含源码分析)

    在Java编程语言中,`HashMap`和`HashTable`都是实现键值对存储的数据结构,但它们之间存在一些显著的区别,这些区别主要体现在线程安全性、性能、null值处理以及一些方法特性上。以下是对这两个类的详细分析: 1. ...

    Java集合专题总结:HashMap 和 HashTable 源码学习和面试总结

    Java集合专题总结:HashMap和HashTable源码...本文总结了HashMap和HashTable的源码学习和面试总结,涵盖了它们的存储结构、构造方法、get和put方法的源码分析、Hash表的特点和缺点、HashTable和HashMap的区别等内容。

    HashMap 源码分析

    然而,HashMap与HashTable的主要区别在于线程安全性:HashTable是线程安全的,而HashMap则不是。 JDK1.8之后,HashMap进行了性能优化,引入了红黑树的数据结构。当链表的长度超过一个阈值(默认为8)时,HashMap会...

    HashMap模块源码—— 高效随机存取数据结构 ,文本索引必备

    2、相比网站上发布过的hashtable之类的源码:。此HashMap寻址方法是拉链法.比开放寻址法对连续内存要求更低。哈希函数用的是java中String.hashCode()算法(经实际验证其碰撞率极低且相近的文本散列值相邻,存取的效率更...

    HashMap源码分析

    `HashMap`允许存储键值对,并且支持使用`null`作为键或值,这与`Hashtable`有所不同,后者不允许键或值为`null`。`HashMap`通过散列表来存储数据,这种设计使得在大多数情况下,其插入、删除和查找操作的时间复杂度...

    Java源码解析HashMap简介

    Java源码解析HashMap简介 HashMap是Java开发中最常用的集合之一,它基于hash表的实现,提供了map的所有可选操作,并且允许null值和一个null的key。HashMap的实现提供了常量级的性能,假设hash函数把元素们比较好的...

    HashMap资料.zip

    9. **HashMap与HashTable的区别**:HashTable是早期的线程安全版本,但它不允许null键和值,且性能较低,已经被ConcurrentHashMap所取代。 10. **面试常见问题**:面试中常问的问题包括HashMap的扩容机制、哈希冲突...

    对java基础集合部分(List、HashMap、HashSet、ArrayList等)底层源码的分析与总结

    HashMap不是线程安全的,如果需要线程安全的Map,可以使用Hashtable。 LinkedHashMap与HashMap类似,但保持了插入顺序或访问顺序。TreeMap使用红黑树,保证了键的排序。 总的来说,理解这些集合的底层实现对于优化...

    HashMap 的底层原理Java系列2021.pdf

    本文将详细探讨HashMap的底层原理,包括其数据结构、存取实现以及与Hashtable的区别。 1. HashMap的数据结构 数据结构是计算机存储、组织数据的方式,它决定了数据的操作效率。HashMap使用数组和链表的组合来实现,...

    java源码整理包-集合

    5. **HashMap与Hashtable的区别**:`HashMap`是非同步的,适合于单线程环境,而`Hashtable`是同步的,可以在多线程环境中安全使用。`HashMap`允许`null`键和值,而`Hashtable`不允许。 6. **TreeMap与TreeSet的特点...

    HashMap源码分析系列-第四弹:HashMap多线程解决方案.docx

    ### HashMap多线程解决方案 #### 一、引言 在多线程环境下,Java的`HashMap`类在处理并发操作时容易出现线程安全问题。本文档深入探讨了`HashMap`在多线程环境中可能遇到的安全问题,并提出了一系列可行的解决方案...

    并发编程atomic&collections-课上笔记1

    本文主要讲述了 Java 中的并发编程,包括 atomic 包的介绍、CAS 算法的原理、ABA 问题的解决方案,以及 collections 中的 HashMap、HashTable 和 ConcurrentHashMap 的源码分析。 Atomic 包的介绍 ----------------...

    深入解读大厂java面试必考点之HashMap全套学习资料

    - HashMap和Hashtable的区别? - HashMap与HashSet的关系? - 如何解决哈希冲突? - 如何自定义键的哈希码生成方式? - 如何避免和处理HashMap中的循环链表? 通过深入学习和理解这些知识点,你将能够在面试中自信...

    java7hashmap源码-JAVA-:JAVA-

    hashmap源码 Notes 原理 basic 1 数据结构中各种东西的数量很很重要!!!可以考虑在在数据结构中定义一下 2 Java hashtable的contains用来查找是否存在value,和containsValue类似 查找key使用containskey方法, 3 ...

    hashtable C++无锁(std_atomic)&U32非0&不可扩大.rar

    在这个“hashtable C++无锁(std_atomic)&U32非0&不可扩大.rar”压缩包中,包含了一个C++实现的无锁哈希表,其关键特性是使用了std::atomic保证线程安全性,并且键值类型为U32(无符号32位整数),并且哈希表的大小不...

    java7hashmap源码-WeishenTemp:WeishenTemp

    hashmap源码 Java 面试随着时间的改变而改变。在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象。因此在每次对 String ...

    易语言-HashMap模块源码—— 高效随机存取数据结构 ,文本索引必备

    2、相比网站上发布过的hashtable之类的源码: 此HashMap寻址方法是拉链法.比开放寻址法对连续内存要求更低 哈希函数用的是java中String.hashCode()算法(经实际验证其碰撞率极低且相近的文本散列值相邻,存取的效率更高...

    Java集合框架源码剖析:HashSet 和 HashMap

     HashMap实现了Map接口,允许放入null元素,除该类未实现同步外,其余跟Hashtable大致相同,跟TreeMap不同,该容器不保证元素顺序,根据需要该容器可能会对元素重新哈希,元素的顺序也会被重新打散,因此不同时间...

    Java集合类源码(摘自jr源码)

    在给定的压缩包文件中,包含了一些关键的集合类源码,如`TreeMap`、`Hashtable`、`ArrayList`、`HashMap`、`LinkedList`、`List`、`Map`、`TreeSet`、`LinkedHashMap`和`Set`。这些类都是Java集合框架的重要组成部分...

Global site tag (gtag.js) - Google Analytics