[译]Top 9 Questions About Java Maps

本篇原文

通常来说,Map是一个由键值对组成的数据结构,并且在一个 map 中,每一个 key 都只能出现一次.这篇文章总结了九个关于如何使用 Java Map 和它的实现类的问题.后文中为了方便,Map的类型都使用的是泛型.因此,这里仅会使用Map而不是指定数据类型的 Map,但是你仍然可以假设KV 都是可排序的.

0.Convert a Map to a List

在 Java 中Map 接口提供了三种 collection views:key set,value set,和 key-value set.这些都可以通过使用构造方法或者allAll()被转换成List.下面这个代码段展示了如何从一个 map 中构造一个ArrayList

// key list
List keyList = new ArrayList(map.keySet());
// value list
List valueList = new ArrayList(map.values());
// key-value list
List entryList = new ArrayList(map.entrySet());

1.Iterate over a Map

迭代访问键值对是一种常见的遍历 map 的方式.在 Java 中,这样的键值对是存在 mapp 的Map.Entry 内部类中.这个内部类返回一个 key-value set,因此遍历一个 map 最有效的方式是

for(Entry entry: map.entrySet()) {
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

同样可以使用迭代器(Iterator)

Iterator itr = map.entrySet().iterator();
while(itr.hasNext()) {
  Entry entry = itr.next();
  // get key
  K key = entry.getKey();
  // get value
  V value = entry.getValue();
}

2.Sort a Map on the keys

如何给map 的 key 进行排序也是一个被频繁提起的问题.一个有效的方式是将 Map.Entry 放在 list 之中,然后用 comparaotr 接口对 list 进行排序

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getKey().compareTo(e2.getKey());
  }

});

另外一个方式是使用SortedMap.如果要使用SortMap 就得要保证所有的 key 都得实现Comparable 接口,或者接收一个Comparator.

SortMap 的一个实现类是TreeMap.它的构造方法可以接收一个Comparator.下面这段代码展示了如何将一个普通的 Map 转成一个可排序的 Map.

SortedMap sortedMap = new TreeMap(new Comparator() {

  @Override
  public int compare(K k1, K k2) {
    return k1.compareTo(k2);
  }

});
sortedMap.putAll(map);

3.Sort a Map on the values

将map 放在list 中排序同样也适用于这种情况,不过这次调用的是Entry.getValue()方法,来看一下下面这段代码:

List list = new ArrayList(map.entrySet());
Collections.sort(list, new Comparator() {

  @Override
  public int compare(Entry e1, Entry e2) {
    return e1.getValue().compareTo(e2.getValue());
  }

});

同样的,仍然可以使用一个 sorted map 来解决这个问题,不过这仅仅适用于所有的 value 也都是唯一的情况.在这种情况下,你可以将键值对倒转使用,就像 key=valuevalue=key.但是这种情形具有太强的限制性,所以这里并不推荐使用

4.Initialize a static/immutable Map

如果你希望你的 map 在代码中保持不变,将它变成一个 immutable map 是一个很不错的方法.这种防御性的代码技术将为你创造一个线程安全并且不会改变的 map.

我们可以通过使用 static 代码块来创建一个 static/immutable map,就像下面这样

public class Test {

  private static final Map map;
  static {
    map = new HashMap();
    map.put(1, "one");
    map.put(2, "two");
  }
}

这段代码的问题是即使 map 被声明成了一个静态常量,仍然可以通过Test.map.put这种方式对 map 进行修改.因此这并不是真正的 immutable.为了能创建一个真的不可变的 map,我们需要一个额外的匿名类,并且将这个匿名类复制到一个不可修改的 map 中,就像下面这段代码一样

public class Test {

  private static final Map map;
  static {
    Map aMap = new HashMap();
    aMap.put(1, "one");
    aMap.put(2, "two");
    map = Collections.unmodifiableMap(aMap);
  }
}

如果对这段代码调用Test.map.put方法,将会抛出一个UnsupportedOperationException 异常.

Guava 库提供了几种不同的方式来创建 static/immutable collection.这里就不展开讲解,如果感兴趣,可以查看他们的git.

5.Difference between HashMap,TreeMap,and Hashtable

Java 中的Map 有三个主要的实现类,它们分别是:HashMap,TreeMap,和Hashtable.下面列举一下这几个实现类最主要的区别:

1.迭代的顺序.HashMapHashtable是不保证取值的顺序就是存值的顺序的,甚至无法保证每次遍历时的顺序(即分两次遍历,返回顺序也可能不同).但是TreeMap的迭代顺序是依据 key 的自然排序或者是给 key 一个Comparator.

2.key-value权限.HashMap允许null keynull value,Hashtable即不允许null key也不允许null value.而TreeMap则是分情况的,如果它使用的是自然排序或者它的comparator不允许null key,则使用空值会抛出异常.

3.Synchronized.只有Hashtable是同步的,其他的都不是.因此,如果不需要线程安全的话,推荐使用HashMap.

如果想了解更多,可以查看这篇文章.

6.A Map with reverse view/lookup

有些时候,我们需要一个key-keyset,这意味着 map 的 value 就像 key 一样,是不重复的.这种约束创造了一种可反转的 map.所以我们可以通过 value 来找 key.这种数据结构被称为双向映射(bidirectional map),但是不幸的是,JDK 并不支持这种 map.

不过 Apache 和 Guava 提供的工具类都有这种双向映射的实现,分别是BidiMapBiMap.它们都强制约束了 key 与 value 之间一对一的关系

7.Shallow copy of a Map

Java 提供的大多数 Map 的实现类都提供了一个复制其他map的构造方法.但是这个复制的过程不是线程安全的.这意味着当一个线程在复制一个 map,另外一个可能在修改它的结构.为了避免这种不同步的意外发生,map 需要提前使用Collections.synchronizedMap方法

Map copiedMap = Collections.synchronizedMap(map);

另外一个浅拷贝的方法是使用clone().然鹅甚至 Java 集合框架的设计者都不建议使用这种方式

I often provide a public clone method on concrete classes because people expect it. ... It's a shame that Cloneable is broken, but it happens. ... Cloneable is a weak spot, and I think people should be aware of its limitations.

正是由于这个原因,这里就不讲如何通过clone()方法来实现map 的浅复制了.

8.Create an empty Map

如果要创建一个 immutable map,可以使用

map = Collections.emptyMap();

否则,可以使用任一一个实现类,比如

map = new HashMap();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,293评论 18 399
  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,213评论 0 16
  • Java SE 基础: 封装、继承、多态 封装: 概念:就是把对象的属性和操作(或服务)结合为一个独立的整体,并尽...
    Jayden_Cao阅读 2,048评论 0 8
  • HashMap 是 Java 面试必考的知识点,面试官从这个小知识点就可以了解我们对 Java 基础的掌握程度。网...
    野狗子嗷嗷嗷阅读 6,604评论 9 107
  • Part1 “赵怡!过来,回来了!” 我应声,循着同学的声音,绕过巨大的圆形废水处理池,穿过粗空气廊的拱形钢化玻璃...
    d8dc987602b4阅读 463评论 11 12