Java HashMap keySet()迭代顺序是否一致?

时间:2022-09-18 19:17:03

I understand that the Set returned from a Map's keySet() method does not guarantee any particular order.

我理解从Map的keySet()方法返回的集合并不保证任何特定的顺序。

My question is, does it guarantee the same order 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 not modified, will the iteration over the keySets be in the same order. Using Sun's jdk15 it does iterate in the same order, but before I depend on this behavior, I'd like to know if all JDKs will do the same.

在上面的代码中,假设映射没有被修改,那么对键集的迭代将按照相同的顺序进行。使用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,它将保证order。

11 个解决方案

#1


40  

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?

你可以很容易地得到集合然后自己排序,对吧?

#2


41  

You can use a LinkedHashMap if 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的要慢得多。

#3


9  

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

如果你正在寻找一个特定的类,实现了地图(HashMap,LinkedHashMap,TreeMap等等),那么你可以看到它如何实现键盘()函数来确定行为将通过查看源代码,你真的要仔细看看这个算法属性你正在寻找是否保存(即一致的迭代顺序时,地图没有任何迭代之间插入/删除操作)。例如,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。

#4


6  

The API for Map does not guarantee any ordering 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 SortedMap which provides exactly these guarantees.

编辑——如果您想要依赖于迭代顺序是一致的,那么您需要一个SortedMap来提供这些保证。

#5


5  

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。如果您只是使用映射并碰巧依赖于顺序,那么使用下面的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.

您还可以封装映射(或集合),让它们返回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();
    }
}

#6


3  

Hashmap does not guarantee that the order of the map will remain constant over time.

Hashmap不能保证映射的顺序随时间而保持不变。

#7


2  

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:

不一定。map的keySet函数返回一个集合,集合的迭代器方法在其文档中这样说:

"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.

因此,除非您使用其中一个类并提供保证,否则就没有。

#8


2  

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()返回的映射实现,那么您可以使用LinkedHashMap或TreeMap,并在遍历它们时获得相同的键/值顺序。

#9


1  

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.

从逻辑上来说,如果合同说“没有特定的订单是有保证的”,并且“它一次发出的订单”是一个特定的订单,那么答案是不,你不能指望它会以同样的方式出现两次。

#10


0  

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:

我有一个功能显示地图,打印地图的条目:

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.

你可以看到键的顺序不同。键的排序顺序可以,但是复制映射的键的排序顺序还是和前面的映射一样。我不知道这样说是否正确,但对于两个键相同的hashmap,键的顺序是相同的。这就意味着,如果此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] 

#11


0  

You also can store the Set instance returned by the keySet() method and can use this instance whenever you need the same order.

您还可以存储keySet()方法返回的Set实例,并且可以在需要相同的顺序时使用该实例。

#1


40  

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?

你可以很容易地得到集合然后自己排序,对吧?

#2


41  

You can use a LinkedHashMap if 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的要慢得多。

#3


9  

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

如果你正在寻找一个特定的类,实现了地图(HashMap,LinkedHashMap,TreeMap等等),那么你可以看到它如何实现键盘()函数来确定行为将通过查看源代码,你真的要仔细看看这个算法属性你正在寻找是否保存(即一致的迭代顺序时,地图没有任何迭代之间插入/删除操作)。例如,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。

#4


6  

The API for Map does not guarantee any ordering 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 SortedMap which provides exactly these guarantees.

编辑——如果您想要依赖于迭代顺序是一致的,那么您需要一个SortedMap来提供这些保证。

#5


5  

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。如果您只是使用映射并碰巧依赖于顺序,那么使用下面的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.

您还可以封装映射(或集合),让它们返回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();
    }
}

#6


3  

Hashmap does not guarantee that the order of the map will remain constant over time.

Hashmap不能保证映射的顺序随时间而保持不变。

#7


2  

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:

不一定。map的keySet函数返回一个集合,集合的迭代器方法在其文档中这样说:

"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.

因此,除非您使用其中一个类并提供保证,否则就没有。

#8


2  

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()返回的映射实现,那么您可以使用LinkedHashMap或TreeMap,并在遍历它们时获得相同的键/值顺序。

#9


1  

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.

从逻辑上来说,如果合同说“没有特定的订单是有保证的”,并且“它一次发出的订单”是一个特定的订单,那么答案是不,你不能指望它会以同样的方式出现两次。

#10


0  

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:

我有一个功能显示地图,打印地图的条目:

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.

你可以看到键的顺序不同。键的排序顺序可以,但是复制映射的键的排序顺序还是和前面的映射一样。我不知道这样说是否正确,但对于两个键相同的hashmap,键的顺序是相同的。这就意味着,如果此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] 

#11


0  

You also can store the Set instance returned by the keySet() method and can use this instance whenever you need the same order.

您还可以存储keySet()方法返回的Set实例,并且可以在需要相同的顺序时使用该实例。