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

Java HashMap keySet()迭代顺序是否一致?,java,iteration,hashmap,hashset,Java,Iteration,Hashmap,Hashset,我知道从Map的keySet()方法返回的集合不能保证任何特定的顺序 我的问题是,它是否保证在多次迭代中的顺序相同。比如说 Map<K,V> map = getMap(); for( K k : map.keySet() ) { } ... for( K k : map.keySet() ) { } Map-Map=getMap(); for(K:map.keySet()) { } ... for(K:map.keySet()) { } 在上面的代码中,假设映射未被修改,则

我知道从Map的keySet()方法返回的集合不能保证任何特定的顺序

我的问题是,它是否保证在多次迭代中的顺序相同。比如说

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

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

...

for( K k : map.keySet() )
{
}
Map-Map=getMap();
for(K:map.keySet())
{
}
...
for(K:map.keySet())
{
}
在上面的代码中,假设映射未被修改,则键集上的迭代顺序相同。使用Sun的jdk15,它会以相同的顺序进行迭代,但在我依赖于此行为之前,我想知道是否所有JDK都会这样做

编辑


我从答案中看出我不能依赖它。太糟糕了。我希望不必建立一些新的收藏来保证我的订购。我的代码需要迭代,执行一些逻辑,然后以相同的顺序再次迭代。我将从键集创建一个新的ArrayList,它将保证顺序。

Map的API不保证任何顺序,即使在同一对象上多次调用该方法之间也是如此

在实践中,如果多次后续调用的迭代顺序发生了变化(假设映射本身在两次调用之间没有变化),我会非常惊讶,但您不应该(并且根据API不能)依赖于此


编辑-如果您希望依赖于迭代顺序的一致性,那么您需要一个能够准确提供这些保证的迭代顺序。

从逻辑上讲,如果合同上说“不保证任何特定的顺序”,并且由于“它一次出现的顺序”是一个特定的顺序,那么答案是否定的,您不能指望它以同样的方式出现两次。

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

它不必如此。map的keySet函数返回一个集合,集合的迭代器方法在其文档中说明:

“返回此集合中元素的迭代器。元素不按特定顺序返回(除非此集合是提供保证的某个类的实例)。”


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

如果API文档中没有说明保证,那么您就不应该依赖它。从一个JDK版本到下一个版本,甚至从同一个供应商的JDK,行为都可能发生变化


您可以很容易地获取集合,然后自己对其进行排序,对吗?

Map是一个接口,它在文档中没有定义顺序应该相同。这意味着你不能依赖订单。但是,如果您控制getMap()返回的映射实现,那么您可以使用LinkedHashMap或TreeMap,并在迭代过程中始终获得相同顺序的键/值。

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

如果您正在查看实现Map的特定类(HashMap、LinkedHashMap、TreeMap等),那么您可以看到它是如何实现keySet()函数的,通过检查源代码来确定行为是什么,您必须仔细查看算法,以查看您要查找的属性是否被保留(即,当映射在迭代之间没有任何插入/删除时,保持一致的迭代顺序)

它在不同的JDK之间可能有很大的差异,所以我绝对不会依赖它


也就是说,如果一致的迭代顺序是您真正需要的,那么您可能想要尝试LinkedHashMap。

只是为了好玩,我决定编写一些代码,您可以使用这些代码来保证每次都是随机顺序。这很有用,这样您就可以捕捉到您依赖顺序但不应该依赖顺序的情况。如果您想依赖d的顺序,正如其他人所说,你应该使用一个SortedMap。如果你只是使用一个Map,并且恰好依赖于顺序,那么使用下面的随机迭代器将捕捉到这一点。我只在测试代码时使用它,因为它使用了更多的内存,而不这样做

您还可以包装贴图(或集合),让它们返回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();
    }
}
import java.util.ArrayList;
导入java.util.Collections;
导入java.util.HashMap;
导入java.util.Iterator;
导入java.util.List;
导入java.util.Map;
公共班机
{
专用干管()
{
}
公共静态void main(最终字符串[]args)
{
最终地图项目;
items=newhashmap();
项目。投入(“A”、“1”);
项目。投入(“B”、“2”);
项目。投入(“C”、“3”);
项目。投入(“D”、“4”);
项目。投入(“E”、“5”);
项目。投入(“F”、“6”);
项目。投入(“G”、“7”);
显示(items.keySet().iterator());
System.out.println(“--”);
显示(items.keySet().iterator());
System.out.println(“--”);
显示(新的随机迭代器(items.keySet().iterator());
System.out.println(“--”);
显示(新的随机迭代器(items.keySet().iterator());
System.out.println(“--”);
}
私有静态无效显示(最终迭代器迭代器)
{
while(iterator.hasNext())
{
最后的T项目;
item=iterator.next();
系统输出打印项次(项);
}
}
}
类随机迭代器
实现迭代器
{
私有最终迭代器;
公共随机迭代器(最终迭代器i)
{
最后清单项目;
items=newarraylist();
while(i.hasNext())
{
最后的T项目;
item=i.next();
项目。添加(项目);
}
收藏。洗牌(物品);
迭代器=items.iter
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");

}
public void showMap (Map<Integer, String> map1) {
    for (Map.Entry<Integer,  String> entry: map1.entrySet()) {
        System.out.println("[Key: "+entry.getKey()+ " , "+"Value: "+entry.getValue() +"] ");

    }

}
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] 
    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);
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] 
    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);
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] 
public class Sample3 {

    static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    static SecureRandom rnd = new SecureRandom();

    // from here: https://stackoverflow.com/a/157202/8430155
    static String randomString(int len){
        StringBuilder sb = new StringBuilder(len);
        for (int i = 0; i < len; i++) {
            sb.append(AB.charAt(rnd.nextInt(AB.length())));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        for (int j = 0; j < 10; j++) {
            Map<String, String> map = new HashMap<>();
            Map<String, String> linkedMap = new LinkedHashMap<>();

            for (int i = 0; i < 1000; i++) {
                String key = randomString(8);
                String value = randomString(8);
                map.put(key, value);
            }

            for (String k : map.keySet()) {
                linkedMap.put(k, map.get(k));
            }

            if (!(map.keySet().toString().equals(linkedMap.keySet().toString()) &&
                  map.values().toString().equals(linkedMap.values().toString()))) {
                // never fails
                System.out.println("Failed");
                break;
            }
        }
    }
}
/**
 * The table, initialized on first use, and resized as
 * necessary. When allocated, length is always a power of two.
 * (We also tolerate length zero in some operations to allow
 * bootstrapping mechanics that are currently not needed.)
 */
transient Node<K,V>[] table;
final class Values extends AbstractCollection<V> {
    
    // more code here

    public final void forEach(Consumer<? super V> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.value);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}
final class KeySet extends AbstractSet<K> {

    // more code here

    public final void forEach(Consumer<? super K> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}
if (size > 0 && (tab = table) != null) {
    int mc = modCount;
    for (int i = 0; i < tab.length; ++i) {
        for (Node<K,V> e = tab[i]; e != null; e = e.next)
            action.accept(e.key);               <- from KeySet class
            // action.accept(e.value);          <- the only change from Values class
    }
    if (modCount != mc)
        throw new ConcurrentModificationException();
}