Java HashMap keySet()的迭代次序是否一致?
我明白从Map的keySet()方法返回的Set不保证任何特定的顺序。
我的问题是,它是否保证了多次迭代的相同的顺序。 例如
Map<K,V> map = getMap(); for( K k : map.keySet() ) { } ... for( K k : map.keySet() ) { }
在上面的代码中,假设映射没有被修改,那么对keySet的迭代是否会以相同的顺序进行。 使用Sun的jdk15,它的迭代次序是相同的,但是在我依赖这个行为之前,我想知道所有的JDK是否也会这样做。
编辑
我从答案中看到,我不能依靠它。 太糟糕了。 我希望摆脱不必build立一些新的集合,以保证我的订单。 我的代码需要迭代,做一些逻辑,然后重复迭代相同的顺序。 我只是从keySet创build一个新的ArrayList,这将保证顺序。
如果没有声明在API文档中保证,那么你不应该依赖它。 甚至从同一供应商的JDK,行为甚至可能会从JDK的一个版本变为下一个版本。
你可以很容易地得到的设置,然后就自己sorting吧?
如果你想要一个迭代顺序不变的HashMap,你可以使用LinkedHashMap 。
而且,如果你遍历集合,你应该总是使用它。 迭代HashMap的entrySet或keySet要比LinkedHashMap慢得多。
Map只是一个接口(而不是一个类),这意味着实现它的底层类(有很多)可能会有不同的performance,而API中的keySet()协议并不表示需要一致的迭代。
如果你正在查看一个实现Map的特定类(HashMap,LinkedHashMap,TreeMap等),那么你可以看到它是如何实现keySet()函数来确定什么样的行为将通过签出来源,你必须真的仔细看看algorithm,看看你正在寻找的属性是否保留(也就是说,当迭代之间没有任何插入/移除的时候,迭代次序是一致的)。 例如,HashMap的源代码在这里(打开JDK 6): http : //www.docjar.com/html/api/java/util/HashMap.java.html
从一个JDK到另一个可能会有很大差异,所以我绝对不会依赖它。
这就是说,如果一致的迭代顺序是你真正需要的,你可能想要尝试一个LinkedHashMap。
即使在同一对象上的多个方法调用之间,Map的API也不保证任何顺序。
在实践中,如果迭代顺序改变了多个后续调用(假设地图本身在两者之间没有变化),那么实际上我会感到非常惊讶 – 但是你不应该(而且根据API不能)依靠这个。
编辑 – 如果你想依靠迭代顺序是一致的,那么你需要一个SortedMap ,它提供了这些保证。
为了好玩,我决定写一些你可以用来保证随机顺序的代码。 这是很有用的,这样你就可以根据订单来捕捉你所在的地方,但是你不应该这样做。 如果您要依赖订单,而不像其他人所说的那样,您应该使用SortedMap。 如果你只是使用一个地图,并发生依赖的顺序,然后使用下面的RandomIterator将捕获。 我只能用它来testing代码,因为它使用更多的内存,然后不这样做。
你也可以包装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(); } }
散列图不能保证地图的顺序随着时间的推移将保持不变。
它不一定是。 一个map的keySet函数返回一个Set,这个set的迭代器方法在它的文档中说明了这一点:
“返回这个集合中的元素的迭代器,这些元素以特定的顺序返回(除非这个集合是某个提供担保的类的实例)”。
所以,除非你使用保证类中的一个,否则没有。
Map是一个接口,它没有在文档中定义顺序应该是相同的。 这意味着你不能依靠订单。 但是,如果您控制getMap()返回的Map实现,那么您可以使用LinkedHashMap或TreeMap,并在遍历它们时获得相同的键/值顺序。
从逻辑上讲,如果合同中说“没有特定的订单是有保证的”,而且“一次出来的订单”是一个特定的订单 ,那么答案是否定的,你不能依靠两次出现。
我同意LinkedHashMap的事情。 当我尝试按键对HashMap进行sorting时,只是把我的发现和经验放在一边。
我的代码来创buildHashMap:
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"); }
我有一个函数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() +"] "); } }
现在当我在sorting之前打印地图时,它会打印以下顺序:
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]
这与地图键的放置顺序基本上不同。
现在当我用地图键sorting时:
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]
你可以看到键的顺序的差异。 按键的sorting顺序很好,但是复制地图的按键顺序与先前地图的顺序相同。 我不知道这是否有效,但对于具有相同键的两个hashmap,键的顺序是相同的。 这意味着声明:如果这个JVM版本的HashMap实现,那么由于密钥插入algorithm的固有特性,密钥的顺序不能保证,但对于具有相同密钥的两个映射可以是相同的。
现在,当我使用LinkedHashMap将已sorting的条目复制到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);
输出:
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]
您还可以存储由keySet()方法返回的Set实例,并且可以在需要相同顺序时使用此实例。