翻译人员: 铁锚

翻译时间: 2013年12月12日

原文链接: HashMap
vs. TreeMap vs. Hashtable vs. LinkedHashMap

Map 是最常用的数据结构之一. 

Map 的字面翻译是映射(地图就是一种映射).

本文将为你展示如何使用各种不同的map,包括 HashMap, TreeMap, HashTable 以及 LinkedHashMap.

1. Map 概述

图1

在JavaSE中,对Map的实现主要包括: HashMap, TreeMap, HashTable 和 LinkedHashMap.

如果每个类都用一句话来描述,则表述如下:

  • HashMap 使用哈希表(hash table)实现, 在 keys 和/或 values 之中,都是无序的.
  • TreeMap 基于红黑树(red-black tree)数据结构实现, 按 key 排序.
  • LinkedHashMap 保持者插入顺序.
  • Hashtable 与HashMap实现方式一样,但Hashtable属于同步(synchronized)的.

所以如果代码是线程安全的,那么应该使用HashMap,因为Hashtable的同步是有一定量的运行代价的。而现今对于需要同步的Map,使用 ConcurrentHashMap 也比 Hashtable
有更高的效率。

2. HashMap

如果HashMap中的key使用的是自定义的类对象,那么需要遵守 equals() 与 hashCode() 规范.

class Dog {
	String color;

	Dog(String c) {
		color = c;
	}
	public String toString(){
		return color + " dog";
	}
}

public class TestHashMap {
	public static void main(String[] args) {
		HashMap<Dog, Integer> hashMap = new HashMap<Dog, Integer>();
		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");

		hashMap.put(d1, 10);
		hashMap.put(d2, 15);
		hashMap.put(d3, 5);
		hashMap.put(d4, 20);

		//print size
		System.out.println(hashMap.size());

		//loop HashMap
		for (Entry<Dog, Integer> entry : hashMap.entrySet()) {
			System.out.println(entry.getKey().toString() + " - " + entry.getValue());
		}
	}
}

输出为:

4
white dog – 5
black dog – 15
red dog – 10
white dog – 20

注意看,我们错误地添加了两次"white dogs",但是HashMap 接受了,这严格来说是没意义的,因为现在对"white dogs"的数量产生了混淆.

修正后的 Dog 类如下所示:

class Dog {
    String color;

    Dog(String c) {
        color = c;
    }

    public boolean equals(Object o) {
        return ((Dog) o).color == this.color;
    }

    public int hashCode() {
        return color.length();
    }

    public String toString(){
        return color + " dog";
    }
}

再运行新的代码,结果如下所示:

3
red dog – 10
white dog – 20
black dog – 15

原因在于 HashMap 不运行两个相同的元素作为KEY.

如果没有重写,使用的就会是 Object 类实现的 hashCode() 和 equals() 方法

默认的 hashCode() 方法实现对每个不同的对象返回不同的整数.

默认的 equals() 方法只比较两个引用是否指向同一个实际对象.

如果你还有疑惑,请阅读:  equals()与hashCode()方法协作约定

3. TreeMap

TreeMap是根据key排序的.我们先看下面的示例来加深 "按key排序" 的印象.

class Dog {
	String color;

	Dog(String c) {
		color = c;
	}
	public boolean equals(Object o) {
		return ((Dog) o).color == this.color;
	}

	public int hashCode() {
		return color.length();
	}
	public String toString(){
		return color + " dog";
	}
}

public class TestTreeMap {
	public static void main(String[] args) {
		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");

		TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
		treeMap.put(d1, 10);
		treeMap.put(d2, 15);
		treeMap.put(d3, 5);
		treeMap.put(d4, 20);

		for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
	}
}

执行后结果如下:

Exception in thread “main” java.lang.ClassCastException: collection.Dog cannot be cast to java.lang.Comparable
at java.util.TreeMap.put(Unknown Source)
at collection.TestHashMap.main(TestHashMap.java:35)

既然 TreeMap 是按key排序的,那么key对象就必须可以和另一个对象作比较,因此必须实现 Comparable 接口。

当然,你也可以使用 String 对象作为key,因为 String 类已经实现了 Comparable 接口。

下面,我们修改 Dog 类的代码,使其实现Comparable:

class Dog implements Comparable<Dog>{
	String color;
	int size;

	Dog(String c, int s) {
		color = c;
		size = s;
	}

	public String toString(){
		return color + " dog";
	}

	@Override
	public int compareTo(Dog o) {
		return  o.size - this.size;
	}
}

public class TestTreeMap {
	public static void main(String[] args) {
		Dog d1 = new Dog("red", 30);
		Dog d2 = new Dog("black", 20);
		Dog d3 = new Dog("white", 10);
		Dog d4 = new Dog("white", 10);

		TreeMap<Dog, Integer> treeMap = new TreeMap<Dog, Integer>();
		treeMap.put(d1, 10);
		treeMap.put(d2, 15);
		treeMap.put(d3, 5);
		treeMap.put(d4, 20);

		for (Entry<Dog, Integer> entry : treeMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
	}
}

执行后输出的结果是:

red dog – 10
black dog – 15
white dog – 20

这就是根据key对象排序的结果,此处我们使用了 size(尺寸)来比较 dog.

如果我们把

"Dog d4 = new Dog("white", 10);"

这一行代码替换为

"Dog d4 = new Dog("white", 40);"

那么,执行后的结果为:

white dog – 20
red dog – 10
black dog – 15
white dog – 5

原因是 TreeMap 使用 compareTo() 方法来比较 key对象,不同的 size 就被认为是不同的 dog.

4. Hashtable

根据Java文档, HashMap 类基本上等同于 Hashtable, 区别仅仅在于: HashMap 不是同步的,并且运行 null 值.

5. LinkedHashMap

LinkedHashMap 是 HashMap 的子类. 所以继承了所有 HashMap 的特性,另外, 链表保持了插入的顺序.

我们复制上面的 HashMap 的示例代码,并将HashMap替换为LinkedHashMap:

class Dog {
	String color;

	Dog(String c) {
		color = c;
	}

	public boolean equals(Object o) {
		return ((Dog) o).color == this.color;
	}

	public int hashCode() {
		return color.length();
	}

	public String toString(){
		return color + " dog";
	}
}

public class TestHashMap {
	public static void main(String[] args) {

		Dog d1 = new Dog("red");
		Dog d2 = new Dog("black");
		Dog d3 = new Dog("white");
		Dog d4 = new Dog("white");

		LinkedHashMap<Dog, Integer> linkedHashMap = new LinkedHashMap<Dog, Integer>();
		linkedHashMap.put(d1, 10);
		linkedHashMap.put(d2, 15);
		linkedHashMap.put(d3, 5);
		linkedHashMap.put(d4, 20);

		for (Entry<Dog, Integer> entry : linkedHashMap.entrySet()) {
			System.out.println(entry.getKey() + " - " + entry.getValue());
		}
	}
}

输出结果如下:

red dog – 10
black dog – 15
white dog – 20

区别在于 HashMap输出的结果顺序与插入顺序是无关的. 

下面再次列出使用 HashMap的输出结果以供对比:

red dog – 10
white dog – 20
black dog – 15

相关文章

  1. Java
    Convert Hashtable to Treemap
  2. Frequently
    Used Methods of Java HashMap
  3. Top
    9 questions about Java Maps
  4. equals()与hashCode()方法协作约定

常用Map实现类对比的更多相关文章

  1. Java开发常用Util工具类-StringUtil、CastUtil、CollectionUtil、ArrayUtil、PropsUtil

    字符串工具类 StringUtil.java package com.***.util; /** * StringUtil * @description: 字符串工具类 **/ public clas ...

  2. Android常用的工具类

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Prefe ...

  3. Android常用的工具类(转)

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.Prefer ...

  4. list 、set 、map 粗浅性能对比分析

    list .set .map 粗浅性能对比分析   不知道有多少同学和我一样,工作五年了还没有仔细看过list.set的源码,一直停留在老师教导的:"LinkedList插入性能比Array ...

  5. 2013最新Android常用的工具类整理

    主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Pref ...

  6. Java并发包——线程安全的Map相关类

    Java并发包——线程安全的Map相关类 摘要:本文主要学习了Java并发包下线程安全的Map相关的类. 部分内容来自以下博客: https://blog.csdn.net/bill_xiang_/a ...

  7. commons-collections包中的常用的工具类

    commons-collections包中的常用的工具类 <dependency> <groupId>commons-collections</groupId> & ...

  8. Hutool中那些常用的工具类和方法

    Hutool中那些常用的工具类和方法 Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子.如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有.本文将对Hu ...

  9. DBUtils ResultSetHandeler常用的处理类

    常用的处理类: BeanHandler: //将结果集中第一条记录封装到一个指定的javaBean中 BeanListHandler: //将结果集中每一条记录封装到指定的javaBean中,将这些j ...

随机推荐

  1. 使用WeihanLi.Redis操作Redis

    WeihanLi.Redis Intro StackExchange.Redis 扩展,更简单的泛型操作,并提供一些的适用于业务场景中的扩展 基于 Redis 的五种数据类型扩展出了一些应用: Str ...

  2. 实验与作业(Python)-03 Python程序实例解析

    截止日期: 要求: 下周实验课前上交,做好后在实验课上检查可获取平时分. 做出进阶或选做的的请用清晰的标致标识出来,方便老师批改 本次作业:可提交也可不提交.作业算平时成绩. 本次作业内容量较大,请组 ...

  3. lucene全文检索基础

    全文检索是一种将文件中所有文本与检索项匹配的文字资料检索方法.比如用户在n个小说文档中检索某个关键词,那么所有包含该关键词的文档都返回给用户.那么应该从哪里入手去实现一个全文检索系统?相信大家都听说过 ...

  4. 这是最好的时光,这是最坏的时光 SNAPSHOT

    好久没动笔了,上次憋了好几天码出的文字扔出去,石沉大海,没惊起半点涟漪.这次真不知道能憋出个什么鬼,索性就让思绪飞扬,飞到哪是哪! --题记 此处应有BGM: 少年锦时 赵雷 1.以后真没有暑假喽 2 ...

  5. java实例化对象

    摘要:分享牛,分享牛分享,java类加载机制,java实例化对象,java实例化对象机制,java基础. java是如何实例化对象的呢?以及实例化对象的先后顺序是什么?下面我们以测试的方式说明. 1. ...

  6. gloox配置聊天室

    gloox配置聊天室 (金庆的专栏) gloox是XMPP协议的C++客户端库.以下代码测试创建多人聊天室(MUC), 并进行配置.参照gloox中的muc示例代码.gloox代码示例中没有聊天室的配 ...

  7. Web自动化框架LazyUI使用手册(8)--excel数据驱动详解(ExcelDataProvider)

    概述 框架提供了excel数据驱动方式运行测试用例的工具,本文将针对数据驱动,进行详细演示. 详见类:lazy.test.ui.browser.ExcelDataProvider 被测对象: http ...

  8. Android中的语言和字符串资源

    在任何情况下,从您的应用代码中提取 UI 字符串并将其存放在外部文件中都是个好办法.Android 在每个 Android 项目中都提供一个资源目录,从而简化了这一过程. 如果您是使用 Android ...

  9. 【ShaderToy】水彩画

    写在前面 好久没有更新shadertoy系列了,我万万没想到有童鞋还惦记着它...之前说过希望可以一周更新一篇,现在看来是不怎么可能了,一个月更新一篇的希望比较大(不要再相信我了...) 我把之前实现 ...

  10. 从操作系统内核看Java非阻塞IO事件检测

    非阻塞服务器模型最重要的一个特点是,在调用读取或写入接口后立即返回,而不会进入阻塞状态.在探讨单线程非阻塞IO模型前必须要先了解非阻塞情况下Socket事件的检测机制,因为对于非阻塞模式最重要的事情是 ...