- 浏览: 305836 次
- 性别:
- 来自: 广州
博客专栏
-
多线程深入浅出
浏览量:48228
文章分类
最新评论
-
buffon918:
撸主1024
DB2 SQLSTATE 及其含义 -
学海无涯_小波:
我也在考驾照 累死了,不错 来几篇线程池的
Java线程及线程池专题 -
operthing:
operthing 写道
多线程——同步(synchronized)下 -
operthing:
...
多线程——同步(synchronized)下 -
ciding:
Jacarri_Chan 写道你觉得hibernate的优势和 ...
Hibernate-01-持久层发展过程
Java集合框架——HashMap与Hashtable
先来看看两个类的重点方法,再来比较。(这是我看源代码的思路,你也先去看看吧)
集合最主要的操作:写入,读取,移除。
/** * Associates the specified value with the specified key in this map. * If the map previously contained a mapping for the key, the old * value is replaced. * 接合指定的value与指定的key在这个map中,假如以前在这个map中包含这个key,以前 * 对应的value将会被替换 * @param key key with which the specified value is to be associated * @param value value to be associated with the specified key * @return the previous value associated with <tt>key</tt>, or * <tt>null</tt> if there was no mapping for <tt>key</tt>. * (A <tt>null</tt> return can also indicate that the map * previously associated <tt>null</tt> with <tt>key</tt>.) */ public V put(K key, V 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; }
读源代码时,记得把上面的注释读一下,这是最快的理解方法的方式,不要一看源代码,就想着那几个for。
里面有几个关键点:
1:int hash = hash(key.hashCode());
2:int i = indexFor(hash, table.length);
3:addEntry(hash, key, value, i);
先说第1点:
/** * Applies a supplemental hash function to a given hashCode, which * defends against poor quality hash functions. This is critical * because HashMap uses power-of-two length hash tables, that * otherwise encounter collisions for hashCodes that do not differ * in lower bits. Note: Null keys always map to hash 0, thus index 0. */ 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); }
这个是HashMap的哈唏值的算法:(正好之前看过一篇博文,给出分析图,论坛地址是:http://www.iteye.com/topic/709945)
画的很好,所以就引用一下啊,至于分析的话,还是看原地址中的吧。(我说过,让你与我一起来这个过程,所以自己去看)。
第2点:
/**
* Returns index for hash code h.
*/
static int indexFor(int h, int length) {
return h & (length-1);
}
这么少?那分析什么啊。这里真看不出来有什么好分析的,可是我运气比较好,看过一篇博文。这个地址忘记了,我就把内容写一下吧。
这里的关键点在于length的长度。
它是Entry[] table的长度,这个与HashMap的初始化时指定的。
先看一下HashMap的三个构造函数:
HashMap(int initialCapacity, float loadFactor)
HashMap(int initialCapacity)
HashMap()
从最后一个说起:
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR;
threshold = (int)(DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
table = new Entry[DEFAULT_INITIAL_CAPACITY];
init();
}
其中:DEFAULT_LOAD_FACTOR=0.75;DEFAULT_INITIAL_CAPACITY=16;
这是默认值,平时用的是不是这个呢?(今天看了之后,以后可以不用这个了,因为你要知道它的用意。)
第二个就不说了,直接第一个构造函数吧。(记得看一下注释)
/** * Constructs an empty <tt>HashMap</tt> with the specified initial * capacity and load factor. * * @param initialCapacity the initial capacity * @param loadFactor the load factor * @throws IllegalArgumentException if the initial capacity is negative * or the load factor is nonpositive */ public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); //如果你给出的容量大小大于HashMap支持的最大值时,取最大值 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); //下面的是关键:获取大于或等于的initialCapacity值的最小的2的n次方。 // 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(); }
看到了吧,这里有个2的n次方。回到先前说的indexFor方法
h&(length-1),这下知道道理了吧,保证在数组之内循环。(只能设计者太邪恶了,因为你只有把代码全部看懂才知道这个用处,好在分享者众多。)
再回到上面说的第3重点:addEntry(hash, key, value, i);
/** * Adds a new entry with the specified key, value and hash code to * the specified bucket. It is the responsibility of this * method to resize the table if appropriate. * * Subclass overrides this to alter the behavior of put method. */ 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) resize(2 * table.length); }
这里真没什么好说的,为了分析与Hashtable的不同,看看这个resize(2 * table.length);再到这个方法中的transfer(newTable);(其实这个在Hashtable与这没什么不同,引大家来是想学一下人家的设计思想,都是两个循环,因为HashMap可以有key为null的值,所以设计的时候,就得考虑进去。至于效率方面,两个没什么差别,但是可以看出在扩大容量时,是一个很耗时的工作。认清这点,虽然比较Hashtable没什么用,但是你在理解HashMap有用,与别的集合比较也有用,难道不是吗?)
/** * Transfers all entries from current table to newTable. */ 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) { 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); } } }
到此put方法就结束了。(看客还有什么好的要说的,就回复吧,因为我分享的同时,也希望能再深入点。)
第二大重点,读取
/** * Returns the value to which the specified key is mapped, * or {@code null} if this map contains no mapping for the key. * * <p>More formally, if this map contains a mapping from a key * {@code k} to a value {@code v} such that {@code (key==null ? k==null : * key.equals(k))}, then this method returns {@code v}; otherwise * it returns {@code null}. (There can be at most one such mapping.) * * <p>A return value of {@code null} does not <i>necessarily</i> * indicate that the map contains no mapping for the key; it's also * possible that the map explicitly maps the key to {@code null}. * The {@link #containsKey containsKey} operation may be used to * distinguish these two cases. * * @see #put(Object, Object) */ public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; }
代码本身没什么好说的,这里来看看构造函数对它的影响。
如果每个key的hash值都是不一样的,那就很快;反之,如果多个key的hash值一样,而table的深度就更深,获取的速度就有可能遍历到最后。
如果开始就知道 HashMap 会保存多个 key-value 对,可以在创建时就使用较大的初始化容量,如果 HashMap 中 Entry 的数量一直不会超过极限容量(capacity * load factor),HashMap 就无需调用 resize() 方法重新分配 table 数组,从而保证较好的性能。当然,开始就将初始容量设置太高可能会浪费空间(系统需要创建一个长度为 capacity 的 Entry 数组),因此创建 HashMap 时初始化容量设置也需要小心对待。
再看移除:remove(Object key),与Hashtable没有什么区别的。
都是要遍历一次。并找到对应的key-value,将其移除。所以要它的时间复杂度o(n)。(等之后看了别的集合再说说。)
说完了。到Hashtable了。
构造函数差不多,不说。
不同点:
1,默认值不一样。0.75是一样的,initialCapacity为11。而且没有最大值,最小值是1。与2的30次方=1073741824
为什么没有最大值呢???(等写完这博文再去看看,这是突然想到的。)
2,hash的算法:直接用的int hash = key.hashCode();
而int index = (hash & 0x7FFFFFFF) % tab.length;与HashMap区别不大,因为HashMap支持key为null,而且固定的把第0位给了它,所以通过h&(length-1)把0给去掉。而这里的这个就是直接循环,对于null的支持,总是要点代价的。
3,就是刚说的对null的支持不同,Hashtable是不支持的。如果从源代码中,可以看到
if (value == null) {
throw new NullPointerException();
}
不支持value为null,而通过
int hash = key.hashCode();可以知道不支持key不能为null。
而HashMap支持有一个key为null,而value为null的不限制。
4,就是看看Hashtable在操作方法前都是有个关键字synchronized,不懂的去看我的博客,地址:http://ciding.iteye.com/blog/1300110 (Java多线程及线程池专题)
5,这个不是重点,也顺带说一下,Hashtable有一个contains(Object value)方法与containsValue(Object value)方法一样。而HashMap只有containsValue(Object value)方法。说这一点的原因是,在有的博文中乱写
只是将两个方法合并而已,引起误解也是因为没有看源代码。
先这到了。。。
评论
提供的是同步。
而HashMap也可以利用Collections类的静态的synchronizedMap()方法实现同步
这个同步的,其实还没有分析完。
在早期的jdk的设计中的同步,都是有缺点的,它用的synchronized关键字,在同步方法时,锁定了类对象。
在后期的版本中,同步都是通过lock来实现的,通过同步块实现,ConcurrentHashMap等类就是例子。
所以,像早期的Hashtable与Vector,Stack等都是一些过时的集合类。
本文没有提及这方面,这方面的内容打算放到总结来说明。
另外一点,有点要“破相”,对于集合的全局观还在建立中,真的是手上无招,心里没底。
因为感觉写的还不够好。
思路没有铺的很清楚,分析的不够具体。
只是踩一下,没说出我的弱点,没有收获的感觉很~~~~~~~~```
HashMap与Hashtable的遍历。
Map ma = new HashMap();或Map ma = new Hashtable();
Iterator iter = ma.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
System.out.println("entry.getValue()="+entry.getValue());
System.out.println("entry.getKey()="+entry.getKey());
}
上面的方法是比较推荐的,还有一种很郁闷的方法:
Iterator iterator = hm.keySet().iterator();
while(iterator.hasNext()) {
System.out.println(hm.get(iterator.next()));
}
这方法很不好,点解?(广州话)
在while遍历一次,在hm.get(iterator.next()));的时候遍历一次
而最上面的。在while遍历后,就获取了。效率你懂的。
但是在这个地址:http://zhidao.baidu.com/question/332769610.html中人家说的话,你上去开骂也没机会。
提供的是同步。
而HashMap也可以利用Collections类的静态的synchronizedMap()方法实现同步
发表评论
-
mina框架 出现BufferUnderflowException异常的解决办法
2012-04-18 16:03 8952背景:使用mina进行socket编程,而传输数据大小会出现比 ... -
基于HttpClient 4模拟登录论坛
2012-04-08 23:19 12027package server; import java. ... -
Java集合框架——ArrayList与LinkedList
2011-12-29 11:16 0Java集合框架——ArrayList与LinkedList ... -
Java集合框架——基础接口
2011-12-21 12:23 5016Java集合框架专题(学习的那个过程,请指教) ... -
Java集合框架专题(学习的那个过程,请指教)
2011-12-19 15:02 2233Java集合框架专题 在这里提醒一下自己深入的思路,如 ... -
从JVM类加载的角度分析new String("")问题
2011-12-16 17:09 5956回来写博客了,还好,没有失言。 先告诉一下读者,也许我 ... -
多线程——休眠(sleep)
2011-12-14 10:46 18339先抛出一问题? 都说sleep与yield有哪些哪些的区 ... -
多线程——锁(lock)
2011-12-13 09:16 17132上一讲《多线程——同步(synchronized)下》 ... -
多线程——同步(synchronized)下
2011-12-12 16:12 4017接着上一讲《多线程— ... -
多线程——同步(synchronized)上
2011-12-12 12:20 4901多线程——同步(synchro ... -
多线程——状态转换
2011-12-11 12:18 3840多线程——状态转换 线程可以分为4个状态: New( ... -
实现一个简单的多线程
2011-12-11 10:55 5129一、定义线程 1、扩 ... -
Java线程及线程池专题
2011-12-09 17:18 9938Java多线程及线程池专题 第一部分:介绍多线程 ... -
(更新时间18号10:42)(原创)移动系统上线,全程经验分享
2011-10-17 12:22 2849移动系统上线,全程经验分享 时间:2011-10-18到20 ... -
(转载)Weblogic 各种版本下载
2011-10-16 23:09 4866Weblogic 各种版本下载 http://dev2d ... -
(入门培训)第一章:java开发工具与环境搭建
2011-10-16 22:36 1402java开发工具汇总 开发工具表: JDK 核心包 J ... -
我的Java学习推荐书目(转载)
2011-06-22 17:15 986一直有这么个想法,列 ...
相关推荐
Java集合专题总结:HashMap 和 HashTable 源码学习和面试总结
hashmap与hashtable区别 主要是应用于存值的数值对
记得刚毕业那会准备面试,看过不少面试题,里面有个说出HashMap和HashTable不同的题目,我那会面试的时候也遇到不少次这个问题,还隐约记得当时的回答是这样的: HashTable是比较旧的版本;HashTable是线程安全的,...
初级程序员面试经常问道的问题,HashMap与HashTable区别,希望有帮助
HashMap、HashTable和HashSet是Java中常用的数据结构,它们的底层实现原理以及区别如下:HashMap底层实现原理: HashMap基于哈希表(HashTable)实现,它通过散列算法将键值对映射到数组中。在HashMap中,每个键值对...
本篇文章主要介绍了java中HashMap和Hashtable的区别,具有一定的参考价值,有需要的可以了解一下。
hashMap和hashTable的区别,大家可以下载学习学习。
HashTable不支持空键值对! 而HashMap支持空键值对!
Java面试题11.HashMap和HashTable的区别.mp4
hashmap和hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别。...Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
NULL 博文链接:https://qiaolevip.iteye.com/blog/2094447
(多选)有关hashMap跟hashTable的区别,说法正确的是? A. HashMap和Hashtable都实现了Map接口 B. HashMap是非synchronized,而Hashtable是synchronized C. HashTable使用Enumeration,HashMap使用Iterator D. ...
HashMap和Hashtable的区别Java开发Java经验技巧共2页.pdf.zip
hashmap和hashtable的区别
Java集合框架详解非扫描,由网上博客整理而成。对hashmap和hashset进行了比较深入的讲解。
今天小编就为大家分享一篇关于HashMap和HashTable底层原理以及常见面试题,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
java集合在日常开发中经常用到,对基础的掌握尤其重要,其中List,Set,Map的作用以及使用的场景和分类描述,其中Arraylist 与 LinkedList 区别,HashSet与TreeSet与LinkedHashSet对⽐,LinkedHashMap和HashMap,...
以下是对java中ArrayList与Vector的区别以及HashMap与Hashtable的区别进行了详细的解析。需要的朋友可以过来参考下
HashMap,HashTable,LinkedHashMap,TreeMap的区别
hashMap和Hashtable的区别1