`
TimmyWang
  • 浏览: 612 次
  • 性别: Icon_minigender_1
  • 来自: 大连
最近访客 更多访客>>
社区版块
存档分类
最新评论

啃掉笔试面试--Hashtable和HashMap引发的血案

阅读更多

 

人物:

王小胖:性别:男。程序员,工作经验1 year。爱好:吃肉、电玩、马小花。特技:吃肉不用考虑胃的容量。

马小花:性别:女。学生,工作经验0 year。爱好:蛋糕、臭美、王小胖。特技:能够降服王小胖……

 

/**20112月,电影《将爱情进行到底》火得不得了。周末,小胖也陪着小花去看这部电影。放映中,小花被影片中的靖哥哥和杜拉拉感动的一沓糊涂,而小胖则心里暗自后悔没有买一袋大爆米花来打发这无聊的时间。影片结束,小花已经是鼻涕一把泪一把,小胖也只有装模作样地抽动了几下鼻子,一心只想着一会儿是吃麦当劳还是必胜客。*/

 

回到家中,小胖和小花各自玩着电脑。

小花:胖子,你知道HashtableHashMap的区别吗?

小胖:略知。

小花:……装什么!!给我讲讲!!!

小胖:好的……

 

第一个区别就先来说说继承关系吧。

 

  如果你在baidugoogle一下(技术类文章的搜索还是推荐google),会发现网上的大致说法与“由于Java发展的历史原因。Hashtable是基于陈旧的Dictionary类的,HashMapJava 1.2引进的Map接口的一个实现。”相同。这种说法没有错,但是胖子觉得不够准确,特别是对于我们这种大众菜鸟来说,如果不去深究的话,可能就会造成一些理解上的差异。简单的认为Hashtable没有继承Map接口。胖子之前就犯过这样的错误(胖子承认自己笨,是真笨……) 。

 

       小花:那你怎么知道它们两个各自的继承关系呢?胖子。

 

我们可以参考一下最新的JDK1.6的源码,看看这两个类的定义:

 

public class Hashtable<K,V>
    extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {…}
 
public class HashMap<K,V>
    extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {…}

 

 

可以看到hashtable也是继承了Map接口。它们的不同是Hashtablesince JDK1.0)就继承了Dictionary这个抽象类,而HashMapsince JDK1.2)继承的则是AbstractMap这个抽象类。因为在Hashtable中看到继承Map后所实现的方法是JDK1.2版本时加上去的,所以胖子猜想可能是在JDK 1.2开发时Sun工程师出于统一的考虑使得Hashtable也继承了Map接口。

 

       小花:哦,原来JDK源码还能看出来这个。

       小胖:……后面还能看出更多东西的。

       小花:好期待啊。

 

第二个区别我们从同步和并发性上来说说它们两个的不同。

 

可以通过这两个类得源码来分析,Hashtable中的主要方法都做了同步处理,而HashMap则没有。可以说Hashtable在默认情况支持同步,而HashMap在默认情况下是不支持的。我们在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。HashMap的同步处理可以使用Collections类提供的synchronizedMap静态方法;或者直接使用JDK5.0之后提供的java.util.concurrent包里的ConcurrentHashMap类。

 

小胖:synchronizedMap静态方法和ConcurrentHashMap类我会以后再给你详细讲一下的。肥婆。

小花:你保证啊。钥匙忘了你知道后果的。

小胖:好的……

 

第三个区别就是它们对于null值的处理方式了。

 

我们依然能够从源代码中得知,Hashtable中,keyvalue都不允许出现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;
   //…
}
  

在我们使用上面的方法时,如参数valuenull,可以从代码中直接看出程序会抛出NullPointerException;而在keynull时,则会在“int hash = key.hashCode();“这段计算Hash值的过程中抛出NullPointerException而在在HashMap中,允许null作为key存在,并且和其他key的特性一样,这样的nullkey只能有一个;另外HashMap允许多个valuenull。这样大家就要注意了, HashMap中就不能用get(key)方法来判断HashMap中是否存在某个key,因为valuenull和不存在该keyEntry都会返回null值,而应该用containsKey()方法来判断了。

 

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
 
public class TestCase {
 
    public static void main(String[] args) {
       Map<Integer,String> hashMap = new HashMap<Integer,String>();
       hashMap.put(0, null);
       hashMap.put(1, "one");
       hashMap.put(2, "two");
       hashMap.put(null, "null");
       for(Entry<Integer, String> e : hashMap.entrySet()) {
           System.out.println("Key: " + e.getKey() + " -- Value: " + e.getValue());
       }
       System.out.println(hashMap.get(0));
       System.out.println(hashMap.get(4));
       System.out.println("Contains key 0 ? :" + hashMap.containsKey(0));
       System.out.println("Contains key 4 ? :" + hashMap.containsKey(4));
       System.out.println("Contains value null ? :" + hashMap.containsValue(null));
    }
 
}
 

结果:

 

Key: null -- Value: null
Key: 0 -- Value: null
Key: 1 -- Value: one
Key: 2 -- Value: two
null
null
Contains key 0 ? :true
Contains key 4 ? :false
Contains value null ? :true

 

 

HashMap对于nullkey的处理网上有说“null new Object()来代替,其Entry.hashCode=0,而且在取出的时候还会还回null的。”胖子我在读取源码的过程中看到了null值的hash值确实是0 (内部实现的数组中的index也是),但是能力有限没有看到转为new Object()的过程。

 

小花: 原来hashMapcontainsKey还有这么个陷阱,以后肥婆要小心了。

 

第四个不同就是它们两个Hash值的获取方式了。

 

还是通过源代码源代码,Hashtable是直接使用key对象的hash值。

 

 

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();//hashcode
       int index = (hash & 0x7FFFFFFF) % tab.length;
       //…
}
 

 

HashMap则是利用key对象的hash值重新计算一个新的hash值。

 

 

public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());//hashcode
        int i = indexFor(hash, table.length);
        //…
}
 
static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

 

 

小花:胖子,都用了hash算法,你给我讲讲Hash算法吧。

小胖:嗯……以后的,今天我比较忙(其实是不会)。

小花:你是不是不会啊?嘿嘿(坏笑)。

小胖:什么不会……谈下一话题……

 

第五个不同就是HashtableHashMap它们两个内部实现方式的数组的初始大小和扩容的方式。

 

HashMap中内部数组的初始容量是16, 加载因子为0.75,而且数组容量增容后也要是2指数次幂:

 

 

/**
     * The default initial capacity - MUST be a power of two.
     */
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
     * The load factor used when none specified in constructor.
     */
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
 

 

HashTable中的内部数组的初始容量是11,加载因子也是0.75数组的增容方式为(oldCapacity * 2 + 1):

 

 

public Hashtable() {
       this(11, 0.75f);
 }
 
protected void rehash() {
       int oldCapacity = table.length;
       Entry[] oldMap = table;
 
       int newCapacity = oldCapacity * 2 + 1;
       //…
}
 

 

第六个不同我们从它们两个遍历方式的内部实现上来说。

Hashtable HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。

 

小花:IteratorEnumeration的区别是什么啊?给我讲讲。

小胖:我不是说我没有时间嘛,下回的。

小花:我都记下来,省得你给我混过去。(拿起笔开始记账中)

小胖:……(紧张)

 

第七个不同时它们的拷贝构造函数的不同。

 

依然是通过查看源码,可以发现它们两个对于拷贝函数初始容量的不同值。

HashMap的实现是:

 

 

public HashMap(Map<? extends K, ? extends V> m) {
        this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
                      DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
        putAllForCreate(m);
    }
 

 

Hashtable的实现是:

 

 

public Hashtable(Map<? extends K, ? extends V> t) {
       this(Math.max(2*t.size(), 11), 0.75f);
       putAll(t);
    }
 

 


小胖:今天讲的已经很多了。我有点饿了,肥婆。

小花:看你今天的表现这么好。走,带你去吃烤肉去。

小胖:哈哈,肥婆万岁。

 

PS:下面打算写的一些东西

 

 

  1. TreeMap的排序及其他相关集合类
  2. synchronizedMap的使用方式
  3. concurrentMap实现细节和使用
  4. Properties使用说明和 扩展
  5. IteratorEnumeration的区别
  6. Hash算法 的实现 

 

 

 

0
1
分享到:
评论

相关推荐

    HashMap和HashTable的区别和不同

    综上所述,`HashMap`和`HashTable`在多个方面存在差异。选择哪一个取决于特定的应用场景和需求: - 如果需要线程安全并且能够接受一定的性能损耗,可以选择`HashTable`。 - 如果追求更高的性能且可以自己处理线程...

    hashtable和hashmap的区别

    ### Hashtable和HashMap的区别 在Java编程语言中,`Hashtable`和`HashMap`是两种非常重要的数据结构,它们都实现了`Map`接口,用于存储键值对。尽管它们有着相似的功能,但在实现细节和应用场景上存在显著差异。接...

    经典讲解List和ArrayList和Vector和HashTable和HashMap区别

    在Java编程语言中,集合框架是处理对象数组的重要工具,其中`List`、`ArrayList`、`Vector`、`HashTable`和`HashMap`是五个关键的接口和类,它们各有不同的特性和用途。以下是这些概念的详细解释: 1. **List接口**...

    Hashtable和HashMap的区别:

    在Java编程语言中,`Hashtable` 和 `HashMap` 都是用来存储键值对的数据结构。这两种数据结构虽然相似,但是在实现细节上存在显著差异。 1. **Hashtable**:作为 `Dictionary` 类的子类,`Hashtable` 是 Java 最早...

    hashMap和hashTable的区别

    ### hashMap和hashTable的区别 #### 一、简介与基本概念 `HashMap` 和 `HashTable` 都是 Java 集合框架中非常重要的数据结构,它们都实现了 `Map` 接口,用于存储键值对。尽管它们在功能上有很多相似之处,但在...

    Hashtable和HashMap区别

    在Java编程语言中,`Hashtable`和`HashMap`是两种非常重要的数据结构,它们都属于`Map`接口的实现类,用于存储键值对数据。尽管两者在功能上相似,但在实际应用中却存在显著差异。 #### 1. 历史与实现基础 `...

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

    Java集合专题总结:HashMap和HashTable源码学习和面试总结 本文总结了Java集合专题中的HashMap和HashTable,涵盖了它们的源码学习和面试总结。HashMap是一种基于哈希表的集合类,它的存储结构是一个数组,每个元素...

    HashMap和HashTable底层原理以及常见面试题

    HashMap和HashTable底层原理以及常见面试题 HashMap和HashTable是Java中两个常用的数据结构,都是基于哈希表实现的,但它们之间存在着一些关键的区别。本文将深入探讨HashMap和HashTable的底层原理,并总结常见的...

    HashMap与HashTable区别

    在Java编程语言中,`HashMap`和`HashTable`是两种非常重要的数据结构,它们都实现了`Map`接口,并提供了键值对的存储方式。这两种数据结构虽然相似,但在实现细节和使用场景上存在显著差异。下面将详细介绍`HashMap`...

    比较分析Vector、ArrayList和hashtable hashmap数据结构

    比较分析Vector、ArrayList和hashtable hashmap数据结构

    开源项目-spion-hashtable-latencies.zip

    开源项目-spion-hashtable-latencies.zip,Improved latency in Go's next version (1.8) at spion/hashtable-latencies

    比较Vector、ArrayList和hashtable hashmap

    - HashMap 和 Hashtable 都实现了 Map 接口,HashMap 更快但不是线程安全的,而 Hashtable 是线程安全但较慢。WeakHashMap 则使用弱引用作为键,有助于防止内存泄漏。 - 在选择使用哪种数据结构时,需要考虑性能需求...

    hashmap与hashtable区别

    在Java编程语言中,`HashMap`和`Hashtable`是两种非常重要的数据结构,它们都用于存储键值对。然而,在实际应用过程中,这两种数据结构有着本质的不同,下面将详细介绍这些差异。 #### 1. 历史背景及实现原理 - **...

    HashMap与HashTable和HashSet的区别

    ### HashMap与HashTable和HashSet的区别 #### 一、概述 在Java集合框架中,`HashMap`, `HashTable` 和 `HashSet` 是三个重要的数据结构,它们分别实现了`Map`接口和`Set`接口,提供了不同的功能来满足不同的编程...

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

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

    HashTable和HashMap的区别_动力节点Java学院整理

    HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有...

    List、ArrayList、Vector及map、HashTable、HashMap分别的区别

    HashMap和Hashtable是两种常用的Map实现类。HashMap是一个非同步的Map实现类,它允许null,即null value和null key。Hashtable是一个同步的Map实现类,它不允许null。 List、ArrayList、Vector及map、HashTable、...

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

    HashMap 和 Hashtable 是 Java 中两种常用的哈希表数据结构,它们都是用来存储键值对的数据结构,但它们在设计和实现上有显著的区别。以下是对这两者差异的详细解释: 1. **线程安全性**: - `Hashtable` 是线程...

    HashMap底层实现原理HashMap与HashTable区别HashMap与HashSet区别.docx

    总的来说,HashMap、HashTable和HashSet各有其特点和适用场景。HashMap适合单线程环境和对性能要求高的场合,HashTable适用于需要线程安全但对性能要求不那么高的情况,而HashSet则是存储不重复元素的高效选择。了解...

    125条常见的java面试笔试题大汇总

    在进行Java开发或准备面试时,理解`HashMap`和`Hashtable`之间的区别是非常重要的。本文将深入探讨这两个类的关键特性及其应用场景,帮助读者更好地掌握这两者的差异。 #### 1. 基本概念与背景 - **`HashMap`** 和...

Global site tag (gtag.js) - Google Analytics