[译]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();

你可能感兴趣的:([译]Top 9 Questions About Java Maps)