新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论Java, J2SE, J2ME, J2EE, 以及Eclipse, NetBeans, JBuilder等Java开发环境,还有JSP, JavaServlet, JavaBean, EJB以及struts, hibernate, spring, webwork2, Java 3D, JOGL等相关技术。
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 Java/Eclipse 』 → Hashmap与Hashtable的选择使用 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7786 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: Hashmap与Hashtable的选择使用 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     菜籽 帅哥哟,离线,有人找我吗?双鱼座1981-2-28
      
      
      威望:5
      头衔:软件民工
      等级:研二(Sowa的知识表示写得真好!)
      文章:875
      积分:5655
      门派:XML.ORG.CN
      注册:2004/7/25

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给菜籽发送一个短消息 把菜籽加入好友 查看菜籽的个人资料 搜索菜籽在『 Java/Eclipse 』的所有贴子 点击这里发送电邮给菜籽 引用回复这个贴子 回复这个贴子 查看菜籽的博客楼主
    发贴心情 Hashmap与Hashtable的选择使用

    1.问题
      Hashtable和Hashmap是我们在开发过程中经常用来映射key到value的容器,在这两者之间选择使用的时候,我们经常被有经验者建议用Hashmap,但我们可能对其中的缘由不甚了解。本文通过对原代码的一些简单分析,来解释原理,从而在使用中能够更好地做出选择。

    2.分析
      Hashtable和Hashmap实现的功能基本相同,但主要有3点区别。

      2.1
      第一个不同之处在于它们的继承关系有所不同。
      public class Hashtable extends Dictionary
      public class HashMap extends AbstractMap

      由上面的代码可以看出Hashtable是基于陈旧的Dictionary类的。在Java 1.2引入Map借口后,Hashtable也改进为可以实现 Map。HashMap是Map接口的一个实现,继承于较新的AbstractMap类。 Hashmap可以算作是Hashtable的升级版本,整体上Hashmap对Hashtable类优化了代码。比如说, 消除了hardcoding,增加了code reuse等等。当然这点不同,并不会对我们选择使用哪个产生影响。

      2.2
      第二个不同,在Hashmap中,null可以作为key,这样的key只有一个,可以有一个或多个key所对应的value为null。而在Hashtable中,null不可以作为key,也不可以作为value。否则会抛出java.lang.NullPointerException。
      Hashtable的put方法的源代码如下:
      public synchronized Object put(Object key, Object 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 e = tab[index] ; e != null ; e = e.next) {
      if ((e.hash == hash) && e.key.equals(key)) {
      Object old = e.value;
      e.value = value;
      return old;
      }
    }
      }

      从这段代码可以看出,在调用Hashtable的put方法时,首先会对put的value是否为空进行判断,如果为空,则会抛出NullPointerException,处理终止。如果对Hashtable中put一个null的key,代码在执行到
      int hash = key.hashCode(); 的时候,因为key对象为空,同样会抛出NullPointerException,处理终止。
      所以在编码时,诸如
      hashtable.put("1", null);//value为null
      或者
      hashtable.put(null, "one");//key为null
      都是错误的。
      当然,平时在编码时,put的key或者value不会那么明显就是个直接的字符串或者其他对象类型。它们可能是从DB或DTO中取得的或者通过一些计算或转化得到的。这个时候要注意put的key或value是否有可能为null,如果有null的可能性,应该先进行判断并处理。以避免异常的产生。

      看看下面这段错误代码。TABLEWORK是个hashtable
      WorkData wd = null;
      wd = rs.getString("HINBAN_NO");// DBから品番を取得する
      TABLEWORK.put(tableName, wd); // テーブルにセットする
      wd对象是从数据库取得的,有可能会为空。所以在向Hashtable中put的时候,要对wd对象是否为空进行判断并对wd对象为空的情况进行处理。

      修改后的代码如下。
      WorkData wd = null;
      wd = rs.getString("HINBAN_NO");// DBから品番を取得する
      if (wd == null) {
    // 存在しなければ、新規作成
    wd = ComUtils.crtHinaban();
      }
      // テーブルにセットする
      TABLEWORK.put(tableName, wd);

      接下来看看Hashmap的put方法是如何对null处理的。
      public Object put(Object key, Object value) {
        Object k = maskNull(key);
        int hash = hash(k);
        int i = indexFor(hash, table.length);

        for (Entry e = table[i]; e != null; e = e.next) {
            if (e.hash == hash && eq(k, e.key)) {
                Object oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, k, value, i);
        return null;
      }

      在put方法的开始,没有像Hashtable那样对value为空进行判断并抛出异常。另外Object k = maskNull(key); 这句代码中的maskNull方法也对put的key进行如下处理。

      static final Object NULL_KEY = new Object();

      /**
      * Returns internal representation for key. Use NULL_KEY if key is null.
      */
      static Object maskNull(Object key) {
         return (key == null ? NULL_KEY : key);
      }
      由此可以看出,在Hashmap put的时候,maskNull()方法会对put的值是否为空进行判断,如果为空,会产生一个新的对象(Object     NULL_KEY = new Object();)即NULL_KEY。

      另外还要注意一个问题,因为Hashmap可以存入null。所以当get()方法返回null值时,既可以表示 Hashmap中没有该key,也可以表示该key所对应的value为null。因此,在Hashmap中不能由get()方法来判断Hashmap中是否存在某个key, 而应该用containsKey()方法来判断。

      看下面这段代码
    public static void main(String[] args) {

      Map map = new HashMap();
      map.put("1", null);
      System.out.println(map.get("1"));
      System.out.println(map.get("2"));
      System.out.println(map.containsKey("1"));
      System.out.println(map.containsKey("2"));
       
    }

      输出结果
      null
      null
      true
      false

      从上面这段试验代码的输出结果可以看出,如果通过get方法是无法判断key值1,2是否存在的,因为它们的返回的值都是null。正确的做法应该使用containsKey()方法来判断,从输出结果看,前者为true,后者为false。

      2.3
      第三个不同是Hashtable的方法是同步的,而Hashmap方法不是。Hashtable是synchronized,你可以不用采取任何特殊的行为就可以在一个多线程的 应用程序中用一个Hashtable,而Hashmap的读写是unsynchronized, 在多线程的环境中要注意使用。这两者的不同是通过在读写方法上加synchronized关键字来实现的.有兴趣的话,大家可以看下Hashtable的源代码,在需要线程安全的方法前都加上了synchronized关键字。下面举了最常用的几个方法的定义。
      hashtable
      public synchronized Object get(Object key)
      public synchronized Object put(Object key, Object value)
      public synchronized Object remove(Object key)
      public synchronized void clear()

      从Hashmap的源代码可以看出没有这个关键字
      hashMap
      public V put(K key, V value)
      public V get(Object key)
      有人可能会问, 既然能synchronized,能线程安全好啊。为什么不要呢,这里其实还是一个效率的问题。对于线程安全的方法,系统要进行加锁,减锁操作。性能会有很大的影响。由于很多程序是在单线程或者说是线程安全的情况下工作的, 所以用synchronized就显得有些多余了。
      当既要同步又要可以让null作为键或者值的时候,一个简便的方法就是利用Collections类的静态的 synchronizedMap()方法,
      Map synMap = Collections.synchronizedMap(map);
      它创建一个线程安全的Map对象,并把它作为一个封装的对象来返回。  


    3. 总结
      通过上述的内容,我们了解了Hashmap和Hashtable的几个主要的不同点。
      Hashmap可以使用null作为key和value,而Hashtable不行。Hashtable是同步的,Hashmap是异步的。但是,因为在需要时,Hashmap可以利用Collections类的静态的 synchronizedMap()方法来实现同步,其次Hashmap的功 能比Hashtable的功能更多,而且它不是基于一个陈旧的类的,所以才有人认为,在各种情况下,Hashmap都优先于Hashtable。


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    重拾英语...

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2008/10/27 20:51:00
     
     GoogleAdSense双鱼座1981-2-28
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Java/Eclipse 』的所有贴子 点击这里发送电邮给Google AdSense 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/9/21 10:37:36

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    78.125ms