回想一下,Map是由一组键值对组成的结构化数据,每个键在单个 Map 中只能使用一次。本主题涵盖有关在 Java 中使用Map及其实现类的 9 个基本问题。为简单起见,我将在示例中使用概括。因此,我将简单地编写Map,而不指定Map说明符。但你可以假设K和V的值都是可比较的,这意味着K扩展了Comparable,而V也扩展了Comparable。
0. 将地图转换为列表
在Java中,Map接口提供了三种集合:键集、值集和键值集。所有这些都可以使用构造函数或方法转换为列表addAll()
。以下代码片段演示了如何从 Map 创建ArrayList 。
// list of keys
List keyList = new ArrayList(Map.keySet());
//list of values
List valueList = new ArrayList(Map.valueSet());
//list key-value
List entryList = new ArrayList(Map.entrySet());
1.循环遍历Map中的所有值
遍历每个键值对是遍历 Map 的最基本、最基本的过程。在 Java 中,每对都存储在名为Map.Entry的 Map 字段中。Map.entrySet()
返回一组键值,因此迭代 Map 的所有值的最有效方法是:
for(Entry entry: Map.entrySet()) {
//get the key
K key = entry.getKey();
//get value
V value = entry.getValue();
}
我们也可以使用Iterator
,特别是在低于 JDK 1.5 的版本中
Iterator itr = Map.entrySet().iterator();
while(itr.hasNext()) {
Entry entry = itr.next();
//get the key
K key = entry.getKey();
//get value
V value = entry.getValue();
}
2. 按键排序映射
按键组织映射是另一个常用的过程。第一种方法是将Map.Entry添加到列表中,并使用按值排序的比较器进行排序。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,此外,它还按顺序排列其键。但是,所有键都必须体现Comparable或被比较器接受。已实现的类之一SortedMap
是TreeMap。它的构造函数接受一个比较器。下面的代码展示了如何将普通的变成Map
有序的。
SortedMap sortedMap = new TreeMap(new Comparator() {
@Override
public int compare(K k1, K k2) {
return k1.compareTo(k2);
}
});
sortedMap.putAll(Map);
3. 按值排序映射
在这种情况下,将地图添加到列表中然后对其进行排序是可行的,但这次您需要使用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());
}
});
在这种情况下我们仍然可以使用它SortedMap
,但前提是值是唯一的。这种情况下,可以将键值对变成键值。该解决方案有严重的局限性,我不推荐。
4. 初始化静态/不可变Map
当您希望 Map 保持不可变时,一个好方法是将其复制到不可变 Map。这种防御性编程技术将帮助您创建一个不仅使用安全而且线程安全的 Map。要初始化静态/不可变映射,我们可以使用初始化器static
(见下文)。这段代码的问题在于,尽管将 Map 声明为static final
,但我们仍然可以在初始化后使用它,例如Test.Map.put(3,"three");
。所以这并不是真正的不变性。要使用静态初始化器创建不可变的 Map,我们需要一个超级匿名类,我们将在最后的初始化步骤中将其添加到不可变的 Map 中。请看代码的第二部分。当运行时会抛出UnsupportedOperationExceptionTest.Map.put(3,"three");
。
public class Test {
private static final Map Map;
static {
Map = new HashMap();
Map.put(1, "one");
Map.put(2, "two");
}
}
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);
}
}
Guava 库还支持各种方式来初始化静态和不可变集合。要了解有关 Guava 不可变集合实用程序的优势的更多信息,请参阅Guava How-to 中的不可变集合部分。
5.HashMap、TreeMap、Hashtable的区别
Java 中Map接口的三个主要实现: HashMap、TreeMap和Hashtable。主要区别如下:- 通过顺序。HashMap和HashTable不保证Map的顺序;特别是,他们不保证订单随着时间的推移保持不变。但
TreeMap
它会按照键的“自然顺序”或通过比较器对所有值进行排序。 - 有效的键值对。
HashMap
允许您有一个空键和一个空值。HashTable
不允许空键或空值。如果TreeMap
使用自然顺序或者比较器不允许空键,则会抛出异常。 - 同步。仅
HashTable
同步,其余不同步。但是,“如果不需要线程安全的实现,建议使用HashMap
”HashTable
。
. | HashMap | HashTable | TreeMap
-------------------------------------------------------
Упорядочивание |нет |нет | да
null в ключ-meaning | да-да | нет-нет | нет-да
синхронизировано | нет | да | нет
производительность | O(1) | O(1) | O(log n)
воплощение | корзины | корзины | красно-чёрное дерево
阅读有关HashMap 与关系的更多信息。树状图对比 哈希表对比 LinkedHashMap。
6. 具有反向搜索/查看功能的地图
有时,我们需要一组键-键对,这意味着值与键一样唯一(一对一模式)。这种一致性允许您在地图上创建“反向视图/搜索”。也就是说,我们可以通过它的值找到一个键。这种数据结构称为双向 Map,不幸的是 JDK 不支持它。Apache Common Collections 和 Guava 都提供双向 Map 实现,分别称为 BidiMap 和 BiMap。两者都引入了强制键和值之间 1:1 映射的约束。7.Map的浅拷贝
几乎所有(如果不是全部)Java 中的 Map 都包含另一个 Map 的复制构造函数。但复制过程不同步。这意味着当一个线程复制 Map 时,另一个线程可以更改其结构。为了防止复制突然不同步,在这种情况下应该使用其中之一Collections.synchronizedMap()
。
Map copiedMap = Collections.synchronizedMap(Map);
另一种有趣的浅复制方法是使用clone()
. 但即使是 Java 集合框架的创建者 Joshua Bloch 也不推荐这样做。在“复制构造函数与克隆”辩论中,他采取的立场是: 引用:“我经常在具体类中提供公共克隆方法,因为人们期望它们在那里......克隆被破坏是一种耻辱,但它发生了……克隆是一个弱点,我认为人们应该警惕它的局限性。” 因此,我什至不向您展示如何使用clone()
复制 Map 的 方法
8. 创建一个空地图
如果Map
不可变,请使用:
Map = Collections.emptyMap();
或者,使用任何其他实施例。例如:
Map = new HashMap();
结尾
GO TO FULL VERSION