如果我有一个用Java实现Map接口的对象,并且我希望对其中包含的每一对进行迭代,那么最有效的方法是什么?
元素的顺序是否取决于我对接口的特定映射实现?
如果我有一个用Java实现Map接口的对象,并且我希望对其中包含的每一对进行迭代,那么最有效的方法是什么?
元素的顺序是否取决于我对接口的特定映射实现?
当前回答
如果我有一个用Java实现Map接口的对象,并且我希望对其中包含的每一对进行迭代,那么最有效的方法是什么?
如果循环键的效率是应用程序的优先事项,那么选择一个Map实现,以您所需的顺序维护键。
元素的顺序是否取决于我对接口的特定映射实现?
是的,绝对。
一些Map实现承诺一定的迭代顺序,而其他的则没有。Map的不同实现维护键值对的不同顺序。
请参见我创建的总结了与Java11捆绑的各种Map实现的表。具体来说,请注意迭代顺序列。单击/轻按以缩放。
您可以看到,有四个Map实现维护一个顺序:
树图并发跳过列表映射链接的哈希映射EnumMap(枚举映射)
NavigableMap界面
其中两个实现NavigableMap接口:TreeMap&ConcurrentSkipListMap。
旧的SortedMap界面被新的NavigableMap界面有效地取代。但您可能会发现第三方实现仅实现旧接口。
自然秩序
如果您想要一个按键的“自然顺序”排列其对的Map,请使用TreeMap或ConcurrentSkipListMap。术语“自然顺序”是指实现Comparable的键类。compareTo方法返回的值用于排序中的比较。
自定义订单
如果要为键指定自定义排序例程以用于维护排序顺序,请传递适合于键类的Comparator实现。使用TreeMap或ConcurrentSkipListMap,传递比较器。
原始插入顺序
如果您希望映射对保持在它们插入映射的原始顺序,请使用LinkedHashMap。
枚举定义顺序
如果使用诸如DayOfWeek或Month之类的枚举作为键,请使用EnumMap类。这个类不仅被高度优化以使用很少的内存并且运行非常快,它还按照枚举定义的顺序维护您的对。例如,对于DayOfWeek,DayOfWeek.MONDAY的键将在迭代时首先找到,DayOfWeek.SUNDAY的密钥将是最后一个。
其他注意事项
在选择Map实现时,还应考虑:
NULLs。某些实现禁止/接受NULL作为键和/或值。并发性。如果要跨线程操作映射,则必须使用支持并发的实现。或者使用Collections::synchronizedMap包装映射(不太可取)。
以上图表中涵盖了这两个考虑因素。
其他回答
package com.test;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("ram", "ayodhya");
map.put("krishan", "mathura");
map.put("shiv", "kailash");
System.out.println("********* Keys *********");
Set<String> keys = map.keySet();
for (String key : keys) {
System.out.println(key);
}
System.out.println("********* Values *********");
Collection<String> values = map.values();
for (String value : values) {
System.out.println(value);
}
System.out.println("***** Keys and Values (Using for each loop) *****");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("Key: " + entry.getKey() + "\t Value: "
+ entry.getValue());
}
System.out.println("***** Keys and Values (Using while loop) *****");
Iterator<Entry<String, String>> entries = map.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry<String, String> entry = (Map.Entry<String, String>) entries
.next();
System.out.println("Key: " + entry.getKey() + "\t Value: "
+ entry.getValue());
}
System.out
.println("** Keys and Values (Using java 8 using lambdas )***");
map.forEach((k, v) -> System.out
.println("Key: " + k + "\t value: " + v));
}
}
Java 8
我们得到了接受lambda表达式的forEach方法。我们也有流API。考虑一张地图:
Map<String,String> sample = new HashMap<>();
sample.put("A","Apple");
sample.put("B", "Ball");
在关键点上重复:
sample.keySet().forEach((k) -> System.out.println(k));
遍历值:
sample.values().forEach((v) -> System.out.println(v));
遍历条目(使用forEach和Streams):
sample.forEach((k,v) -> System.out.println(k + ":" + v));
sample.entrySet().stream().forEach((entry) -> {
Object currentKey = entry.getKey();
Object currentValue = entry.getValue();
System.out.println(currentKey + ":" + currentValue);
});
流的优点是,如果我们需要,它们可以很容易地并行化。我们只需要使用parallelStream()代替上面的stream()。
forEachOrdered与forEach的流?forEach不遵循遭遇顺序(如果已定义),本质上是非确定性的,正如forEachOrdered一样。因此forEach不保证订单会被保留。还要查看此项了解更多信息。
在Map中,可以根据自己的兴趣对键和/或值和/或两者(例如entrySet)进行迭代,比如:
遍历贴图的keys->keySet():Map<String,Object>Map=。。。;for(字符串键:map.keySet()){//您的业务逻辑。。。}遍历映射的values->values():for(对象值:map.values()){//您的业务逻辑。。。}遍历映射的both->entrySet():for(Map.Entry<String,Object>Entry:Map.entrySet()){字符串键=entry.getKey();对象值=entry.getValue();//您的业务逻辑。。。}
此外,有3种不同的方法可以迭代HashMap。具体如下:
//1.
for (Map.Entry entry : hm.entrySet()) {
System.out.print("key,val: ");
System.out.println(entry.getKey() + "," + entry.getValue());
}
//2.
Iterator iter = hm.keySet().iterator();
while(iter.hasNext()) {
Integer key = (Integer)iter.next();
String val = (String)hm.get(key);
System.out.println("key,val: " + key + "," + val);
}
//3.
Iterator it = hm.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Integer key = (Integer)entry.getKey();
String val = (String)entry.getValue();
System.out.println("key,val: " + key + "," + val);
}
这是一个由两部分组成的问题:
如何迭代地图条目-@ScArcher2完美地回答了这个问题。
迭代的顺序是什么?如果您只是使用Map,那么严格来说,没有排序保证。因此,您不应该真正依赖任何实现给出的顺序。然而,SortedMap接口扩展了Map并提供了您所需要的内容——实现将始终提供一致的排序顺序。
NavigableMap是另一个有用的扩展-这是一个SortedMap,它提供了其他方法,用于根据条目在键集中的顺序位置查找条目。因此,这可能会从一开始就消除迭代的需要——在使用higherEntry、lowerEntry、ceilingEntry或floorEntry方法后,您可能能够找到所需的特定条目。descendingMap方法甚至为您提供了一种反转遍历顺序的显式方法。
仅供参考,如果您只对映射的键/值感兴趣,而对其他键/值不感兴趣,那么也可以使用map.keySet()和map.values()。