常用Map实现类对比
翻译人员: 铁锚
翻译时间: 2013年12月12日
原文链接: HashMap
vs. TreeMap vs. Hashtable vs. LinkedHashMap
Map 是最常用的数据结构之一.
Map 的字面翻译是映射(地图就是一种映射).
本文将为你展示如何使用各种不同的map,包括 HashMap, TreeMap, HashTable 以及 LinkedHashMap.
1. Map 概述
在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
相关文章:
- Java
Convert Hashtable to Treemap - Frequently
Used Methods of Java HashMap - Top
9 questions about Java Maps - equals()与hashCode()方法协作约定
常用Map实现类对比的更多相关文章
- Java开发常用Util工具类-StringUtil、CastUtil、CollectionUtil、ArrayUtil、PropsUtil
字符串工具类 StringUtil.java package com.***.util; /** * StringUtil * @description: 字符串工具类 **/ public clas ...
- Android常用的工具类
主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Prefe ...
- Android常用的工具类(转)
主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java.目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils.Prefer ...
- list 、set 、map 粗浅性能对比分析
list .set .map 粗浅性能对比分析 不知道有多少同学和我一样,工作五年了还没有仔细看过list.set的源码,一直停留在老师教导的:"LinkedList插入性能比Array ...
- 2013最新Android常用的工具类整理
主要介绍总结的Android开发中常用的工具类,大部分同样适用于Java. 目前包括HttpUtils.DownloadManagerPro.ShellUtils.PackageUtils. Pref ...
- Java并发包——线程安全的Map相关类
Java并发包——线程安全的Map相关类 摘要:本文主要学习了Java并发包下线程安全的Map相关的类. 部分内容来自以下博客: https://blog.csdn.net/bill_xiang_/a ...
- commons-collections包中的常用的工具类
commons-collections包中的常用的工具类 <dependency> <groupId>commons-collections</groupId> & ...
- Hutool中那些常用的工具类和方法
Hutool中那些常用的工具类和方法 Hutool是一个Java工具包,它帮助我们简化每一行代码,避免重复造轮子.如果你有需要用到某些工具方法的时候,不妨在Hutool里面找找,可能就有.本文将对Hu ...
- DBUtils ResultSetHandeler常用的处理类
常用的处理类: BeanHandler: //将结果集中第一条记录封装到一个指定的javaBean中 BeanListHandler: //将结果集中每一条记录封装到指定的javaBean中,将这些j ...
随机推荐
- Docker Hub
目前 Docker 官方维护了一个公共仓库 Docker Hub,其中已经包括了超过 15,000 的镜像.大部分需求,都可以通过在 Docker Hub 中直接下载镜像来实现. 登录 可以通过执行 ...
- 安卓高级3 RecyclerView结合SwipeRefreshLayout并添加上拉
目录结构: 效果图: MainActivity.java package qianfeng.com.pullrecyclerviewdemo; import android.os.Bundle; im ...
- Linux-2.6.25 TCPIP函数调用大致流程
插口层系统调用send sys_send sys_sendtosendto sys_sendto sock_sendmsgsendmsg sys_send ...
- 【SSH系列】深入浅出spring IOC中三种依赖注入方式
spring的核心思想是IOC和AOP,IOC-控制反转,是一个重要的面向对象编程的法则来消减计算机程序的耦合问题,控制反转一般分为两种类型,依赖注入和依赖查找,依赖什么?为什么需要依赖?注入什么?控 ...
- iOS学习笔记--数据存储
iOS应用数据存储的常用方式 XML属性列表(plist)归档 Preference(偏好设置) NSKeyedArchiver归档(NSCoding) SQLite3 Core Data 1. XM ...
- openresty 备忘
The problem with: apt-get --yes install $something is that it will ask for a manual confirmation if ...
- Python 描述符 data 和 non-data 两种类型
仅包含__get__的,是non-data descriptor, 如果实例__dict__包含同名变量, 则实例优先; 如果还包含__set__, 则是data descriptor, 优先于实例_ ...
- 使用MD5SUM检查文件
有不少网站提供下载文件的同时,提供了文件的MD5SUM的值.如何检查自己下载的文件与原文件一样呢?用md5sum的-c选项. 操作如下: 1.先新建一个文本文件,写入网站上提供的md5sum的值,空两 ...
- socket系列之什么是socket
1.什么是socket Socket是应用层与TCP/IP协议族通信的中间抽象层,它是一组接口,应用层通过调用这些接口实现发送和接收数据.一般这种抽象层由操作系统提供或者由JVM自己实现.使用sock ...
- 协议系列之TCP/IP协议
根据前面介绍的几种协议,将IP协议.TCP协议.UDP协议组合起来,于是便有了TCP/IP协议.现在很多的应用的通信都是建立在TCP/IP协议的基础上,运用非常广泛,很有必要对其学习一下. 打个不太恰 ...