Java HashMap keySet() 迭代顺序是否一致?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1882762/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
is the Java HashMap keySet() iteration order consistent?
提问by karoberts
I understand that the Set returned from a Map's keySet() method does not guarantee any particular order.
我知道从 Map 的 keySet() 方法返回的 Set 不保证任何特定的顺序。
My question is, does it guarantee the sameorder over multiple iterations. For example
我的问题是,它是否保证多次迭代的顺序相同。例如
Map<K,V> map = getMap();
for( K k : map.keySet() )
{
}
...
for( K k : map.keySet() )
{
}
In the above code, assuming that the map is notmodified, will the iteration over the keySets be in the same order. Using Sun's jdk15 it doesiterate in the same order, but before I depend on this behavior, I'd like to know if all JDKs will do the same.
在上面的代码中,假设地图没有被修改,keySet 上的迭代是否会以相同的顺序进行。使用 Sun 的 jdk15 它确实以相同的顺序迭代,但在我依赖这种行为之前,我想知道是否所有 JDK 都会这样做。
EDIT
编辑
I see from the answers that I cannot depend on it. Too bad. I was hoping to get away with not having to build some new Collection to guarantee my ordering. My code needed to iterate through, do some logic, and then iterate through again with the same ordering. I'll just create a new ArrayList from the keySet which will guarantee order.
我从答案中看出我不能依赖它。太糟糕了。我希望不必建立一些新的收藏来保证我的订购。我的代码需要迭代,做一些逻辑,然后以相同的顺序再次迭代。我将从 keySet 创建一个新的 ArrayList 来保证顺序。
采纳答案by Ken Liu
If it is not stated to be guaranteed in the API documentation, then you shouldn't depend on it. The behavior might even change from one release of the JDK to the next, even from the same vendor's JDK.
如果 API 文档中没有声明保证它,那么您不应该依赖它。行为甚至可能会从 JDK 的一个版本更改为下一个版本,即使是来自同一供应商的 JDK。
You could easily get the set and then just sort it yourself, right?
你可以很容易地得到一套,然后自己整理,对吧?
回答by Andrzej Doyle
The API for Map does not guarantee anyordering whatsoever, even between multiple invocations of the method on the same object.
Map 的 API 不保证任何顺序,即使在同一对象上多次调用方法之间也是如此。
In practice I would be very surprised if the iteration order changed for multiple subsequent invocations (assuming the map itself did not change in between) - but you should not (and according to the API cannot) rely on this.
在实践中,如果迭代顺序因多次后续调用而改变(假设地图本身在两者之间没有改变),我会感到非常惊讶 - 但你不应该(并且根据 API 不能)依赖于此。
EDIT - if you want to rely on the iteration order being consistent, then you want a SortedMapwhich provides exactly these guarantees.
编辑 - 如果您想依赖于一致的迭代顺序,那么您需要一个SortedMap来提供这些保证。
回答by Jonathan Feinberg
Logically, if the contract says "no particular order is guaranteed", and since "the order it came out one time" is a particular order, then the answer is no, you can't depend on it coming out the same way twice.
从逻辑上讲,如果合同说“没有保证特定的订单”,并且由于“一次发出的订单”是特定的订单,那么答案是否定的,您不能依赖它两次以相同的方式出现。
回答by Amir Afghani
Hashmap does not guarantee that the order of the map will remain constant over time.
Hashmap 不保证地图的顺序会随着时间的推移保持不变。
回答by Jeff Storey
It doesn't have to be. A map's keySet function returns a Set and the set's iterator method says this in its documentation:
不必如此。地图的 keySet 函数返回一个 Set 并且该 set 的迭代器方法在其文档中说明了这一点:
"Returns an iterator over the elements in this set. The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee)."
“在这个集合中的元素上返回一个迭代器。元素的返回没有特定的顺序(除非这个集合是某个提供保证的类的实例)。”
So, unless you are using one of those classes with a guarantee, there is none.
因此,除非您有保证地使用这些类之一,否则没有。
回答by Andrey Adamovich
Map is an interface and it does not define in the documentation that order should be the same. That means that you can't rely on the order. But if you control Map implementation returned by the getMap(), then you can use LinkedHashMap or TreeMap and get the same order of keys/values all the time you iterate through them.
Map 是一个接口,它没有在文档中定义顺序应该相同。这意味着您不能依赖订单。但是,如果您控制 getMap() 返回的 Map 实现,那么您可以使用 LinkedHashMap 或 TreeMap 并在您遍历它们时始终获得相同顺序的键/值。
回答by mpobrien
Map is only an interface (rather than a class), which means that the underlying class that implements it (and there are many) could behave differently, and the contract for keySet() in the API does not indicate that consistent iteration is required.
Map 只是一个接口(而不是一个类),这意味着实现它的底层类(并且有很多)可能表现不同,并且 API 中的 keySet() 契约并不表明需要一致的迭代。
If you are looking at a specific class that implements Map (HashMap, LinkedHashMap, TreeMap, etc) then you could see how it implements the keySet() function to determine what the behaviour would be by checking out the source, you'd have to really take a close look at the algorithm to see if the property you are looking for is preserved (that is, consistent iteration order when the map has not had any insertions/removals between iterations). The source for HashMap, for example, is here (open JDK 6): http://www.docjar.com/html/api/java/util/HashMap.java.html
如果您正在查看一个实现 Map(HashMap、LinkedHashMap、TreeMap 等)的特定类,那么您可以看到它如何实现 keySet() 函数来通过检查源来确定行为是什么,您必须仔细查看算法,看看是否保留了您正在寻找的属性(即,当映射在迭代之间没有任何插入/删除时,迭代顺序一致)。例如,HashMap 的源代码在这里(打开 JDK 6):http: //www.docjar.com/html/api/java/util/HashMap.java.html
It could vary widely from one JDK to the next, so i definitely wouldn't rely on it.
从一个 JDK 到另一个 JDK 可能会有很大差异,所以我绝对不会依赖它。
That being said, if consistent iteration order is something you really need, you might want to try a LinkedHashMap.
话虽如此,如果您确实需要一致的迭代顺序,您可能想尝试使用 LinkedHashMap。
回答by TofuBeer
Just for fun, I decided to write some code that you can use to guarantee a random order each time. This is useful so that you can catch cases where you are depending on the order but you should not be. If you want to depend on the order, than as others have said, you should use a SortedMap. If you just use a Map and happen to rely on the order then using the following RandomIterator will catch that. I'd only use it in testing code since it makes use of more memory then not doing it would.
只是为了好玩,我决定写一些代码,你可以用它来保证每次的随机顺序。这很有用,这样您就可以捕捉到您依赖订单但不应该如此的情况。如果您想依赖顺序,而不是像其他人所说的那样,您应该使用 SortedMap。如果您只是使用 Map 并且碰巧依赖于顺序,那么使用以下 RandomIterator 将捕获它。我只会在测试代码中使用它,因为它会使用更多的内存,而不是这样做。
You could also wrap the Map (or the Set) to have them return the RandomeIterator which would then let you use the for-each loop.
您还可以包装 Map(或 Set)以让它们返回 RandomeIterator,然后让您使用 for-each 循环。
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Main
{
private Main()
{
}
public static void main(final String[] args)
{
final Map<String, String> items;
items = new HashMap<String, String>();
items.put("A", "1");
items.put("B", "2");
items.put("C", "3");
items.put("D", "4");
items.put("E", "5");
items.put("F", "6");
items.put("G", "7");
display(items.keySet().iterator());
System.out.println("---");
display(items.keySet().iterator());
System.out.println("---");
display(new RandomIterator<String>(items.keySet().iterator()));
System.out.println("---");
display(new RandomIterator<String>(items.keySet().iterator()));
System.out.println("---");
}
private static <T> void display(final Iterator<T> iterator)
{
while(iterator.hasNext())
{
final T item;
item = iterator.next();
System.out.println(item);
}
}
}
class RandomIterator<T>
implements Iterator<T>
{
private final Iterator<T> iterator;
public RandomIterator(final Iterator<T> i)
{
final List<T> items;
items = new ArrayList<T>();
while(i.hasNext())
{
final T item;
item = i.next();
items.add(item);
}
Collections.shuffle(items);
iterator = items.iterator();
}
public boolean hasNext()
{
return (iterator.hasNext());
}
public T next()
{
return (iterator.next());
}
public void remove()
{
iterator.remove();
}
}
回答by ciamej
You can use a LinkedHashMapif you want a HashMap whose iteration order does not change.
如果需要迭代顺序不变的 HashMap,则可以使用LinkedHashMap。
Moreover you should always use it if you iterate through the collection. Iterating over HashMap's entrySet or keySet is much slower than over LinkedHashMap's.
此外,如果您遍历集合,则应始终使用它。遍历 HashMap 的 entrySet 或 keySet 比遍历 LinkedHashMap 慢得多。
回答by Parth Joshi
I agree with LinkedHashMap thing. Just putting my findings and experience while I was facing the problem when I was trying to sort HashMap by keys.
我同意 LinkedHashMap 的事情。当我试图按键对 HashMap 进行排序时,我只是把我的发现和经验放在我面临的问题上。
My code to create HashMap:
我创建 HashMap 的代码:
HashMap<Integer, String> map;
@Before
public void initData() {
map = new HashMap<>();
map.put(55, "John");
map.put(22, "Apple");
map.put(66, "Earl");
map.put(77, "Pearl");
map.put(12, "George");
map.put(6, "Rocky");
}
I have a function showMap which prints entries of map:
我有一个打印地图条目的函数 showMap:
public void showMap (Map<Integer, String> map1) {
for (Map.Entry<Integer, String> entry: map1.entrySet()) {
System.out.println("[Key: "+entry.getKey()+ " , "+"Value: "+entry.getValue() +"] ");
}
}
Now when I print the map before sorting, it prints following sequence:
现在,当我在排序之前打印地图时,它会打印以下序列:
Map before sorting :
[Key: 66 , Value: Earl]
[Key: 22 , Value: Apple]
[Key: 6 , Value: Rocky]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]
Which is basically different than the order in which map keys were put.
这与放置地图键的顺序基本不同。
Now When I sort it with map keys:
现在,当我使用地图键对其进行排序时:
List<Map.Entry<Integer, String>> entries = new ArrayList<>(map.entrySet());
Collections.sort(entries, new Comparator<Entry<Integer, String>>() {
@Override
public int compare(Entry<Integer, String> o1, Entry<Integer, String> o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
HashMap<Integer, String> sortedMap = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println("Putting key:"+entry.getKey());
sortedMap.put(entry.getKey(), entry.getValue());
}
System.out.println("Map after sorting:");
showMap(sortedMap);
the out put is:
输出是:
Sorting by keys :
Putting key:6
Putting key:12
Putting key:22
Putting key:55
Putting key:66
Putting key:77
Map after sorting:
[Key: 66 , Value: Earl]
[Key: 6 , Value: Rocky]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 12 , Value: George]
[Key: 77 , Value: Pearl]
You can see the difference in order of keys. Sorted order of keys is fine but that of keys of copied map is again in the same order of the earlier map. I dont know if this is valid to say, but for two hashmap with same keys, order of keys is same. This implies to the statement that order of keys is not guaranteed but can be same for two maps with same keys because of inherent nature of key insertion algorithm if HashMap implementation of this JVM version.
您可以看到键顺序的差异。键的排序顺序很好,但复制映射的键的排序再次与早期映射的顺序相同。我不知道这样说是否有效,但是对于两个具有相同键的哈希图,键的顺序是相同的。这意味着不能保证键的顺序,但如果此 JVM 版本的 HashMap 实现,则由于键插入算法的固有性质,键的顺序对于具有相同键的两个映射可以相同。
Now when I use LinkedHashMap to copy sorted Entries to HashMap, I get desired result (which was natural, but that is not the point. Point is regarding order of keys of HashMap)
现在,当我使用 LinkedHashMap 将排序的条目复制到 HashMap 时,我得到了想要的结果(这很自然,但这不是重点。重点是关于 HashMap 键的顺序)
HashMap<Integer, String> sortedMap = new LinkedHashMap<>();
for (Map.Entry<Integer, String> entry : entries) {
System.out.println("Putting key:"+entry.getKey());
sortedMap.put(entry.getKey(), entry.getValue());
}
System.out.println("Map after sorting:");
showMap(sortedMap);
Output:
输出:
Sorting by keys :
Putting key:6
Putting key:12
Putting key:22
Putting key:55
Putting key:66
Putting key:77
Map after sorting:
[Key: 6 , Value: Rocky]
[Key: 12 , Value: George]
[Key: 22 , Value: Apple]
[Key: 55 , Value: John]
[Key: 66 , Value: Earl]
[Key: 77 , Value: Pearl]