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

kar*_*rts 71 java iteration hashmap hashset

我知道从M​​ap的keySet()方法返回的Set不保证任何特定的顺序.

我的问题是,它是否保证多次迭代的相同顺序.例如

Map<K,V> map = getMap();

for( K k : map.keySet() )
{
}

...

for( K k : map.keySet() )
{
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,假设映射修改,将迭代在按键组处于相同的顺序.使用Sun的jdk15它以相同的顺序迭代,但在我依赖于这种行为之前,我想知道所有JDK是否都会这样做.

编辑

我从答案中看到我不能依赖它.太糟糕了.我希望不必为了保证我的订购而建立一些新的收藏品.我的代码需要迭代,执行一些逻辑,然后使用相同的顺序再次迭代.我将从keySet创建一个新的ArrayList,这将保证顺序.

cia*_*mej 49

如果需要迭代顺序不变的HashMap,可以使用LinkedHashMap.

此外,如果您遍历集合,则应始终使用它.迭代HashMap的entrySet或keySet比使用LinkedHashMap慢得多.


Ken*_*Liu 47

如果API文档中没有说明保证,那么您不应该依赖它.甚至从同一供应商的JDK,行为甚至可能从JDK的一个版本更改为下一个版本.

您可以轻松获得该设置,然后自己对其进行排序,对吧?

  • 正如其他人提到的,如果你可以控制从getMap()返回哪个Map实例,那么你可以返回一个SortedMap.在这种情况下,您可能希望从getMap()而不仅仅是Map显式返回SortedMap. (2认同)
  • 在Java 7和Java 8之间更改了HashMap和HashSet迭代顺序. (2认同)

mpo*_*ien 9

Map只是一个接口(而不是一个类),这意味着实现它的底层类(并且有许多)可能表现不同,API中keySet()的契约并不表示需要一致的迭代.

如果您正在查看实现Map(HashMap,LinkedHashMap,TreeMap等)的特定类,那么您可以看到它如何实现keySet()函数以通过检查源来确定行为,您必须确实仔细查看算法,看看你要查找的属性是否被保留(即,当地图在迭代之间没有任何插入/删除时,一致的迭代顺序).例如,HashMap的源代码在这里(打开JDK 6):http://www.docjar.com/html/api/java/util/HashMap.java.html

从一个JDK到另一个JDK,它可能有很大差异,所以我绝对不会依赖它.

话虽这么说,如果一致的迭代顺序是你真正需要的东西,你可能想尝试一个LinkedHashMap.


And*_*yle 7

即使在同一对象上多次调用方法之间,Map for Map也不保证任何顺序.

在实践中,如果迭代顺序为多个后续调用而改变(假设地图本身之间没有变化),我会非常惊讶 - 但你不应该(并且根据API不能)依赖于此.

编辑 - 如果你想依赖迭代顺序是一致的,那么你需要一个提供这些保证的SortedMap.


Tof*_*eer 5

只是为了好玩,我决定写一些代码,你可以用它来保证每次随机顺序.这很有用,因此您可以捕获依赖于订单的情况,但您不应该这样.如果你想依赖顺序,而不是像其他人所说的那样,你应该使用SortedMap.如果您只是使用Map并碰巧依赖于订单,那么使用以下RandomIterator将捕获它.我只会在测试代码时使用它,因为它会使用更多的内存然后不会这样做.

你也可以包装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();
    }
}
Run Code Online (Sandbox Code Playgroud)