является ли порядок итерации Java HashMap keySet() последовательным?


Я понимаю, что набор, возвращенный из метода keySet () карты, не гарантирует никакого конкретного порядка.

мой вопрос в том, гарантирует ли он то же самое заказ на несколько итераций. Например

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

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

...

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

В приведенном выше коде, предполагая, что карта не изменено, будет ли итерация по наборам ключей в том же порядке. Используя jdk15 Солнца это тут повторите в том же порядке, но прежде чем я буду зависеть от этого поведение, я хотел бы знать, будут ли все JDKs делать то же самое.

EDIT

Я вижу из ответов, что я не могу зависеть от него. Очень плохо. Я надеялся уйти от необходимости создавать новую коллекцию, чтобы гарантировать мой заказ. Мой код должен был повторять, делать некоторую логику, а затем повторять снова с тем же порядком. Я просто создам новый ArrayList из набора ключей, который будет гарантировать порядок.

11 59

11 ответов:

Если в документации API не указано, что это гарантируется, то вы не должны зависеть от него. Поведение может даже измениться от одного выпуска JDK к следующему, даже от JDK того же поставщика.

вы могли бы легко получить и просто решите это сами, верно?

можно использовать LinkedHashMap Если вы хотите хэш-карту, порядок итераций которой не изменяется.

кроме того, вы всегда должны использовать его, если вы повторяете через коллекцию. Итерация по набору entrySet или keySet HashMap намного медленнее, чем по LinkedHashMap.

Map-это только интерфейс (а не класс), что означает, что базовый класс, который его реализует (и их много), может вести себя по-разному, а контракт для keySet () в API не указывает на то, что требуется последовательная итерация.

Если вы смотрите на определенный класс, который реализует Map (HashMap, LinkedHashMap, TreeMap и т. д.), то вы можете увидеть, как он реализует функцию keySet (), чтобы определить, каково будет поведение, проверяя Источник, вам нужно будет действительно внимательно посмотреть на алгоритм, чтобы увидеть, сохраняется ли свойство, которое вы ищете (то есть согласованный порядок итераций, когда карта не имела никаких вставок/удалений между итерациями). Источник для HashMap, например, находится здесь (open JDK 6):http://www.docjar.com/html/api/java/util/HashMap.java.html

Он может сильно отличаться от одного JDK к другому, поэтому я определенно не буду полагаться на него.

Это, как говорится, если последовательный порядок итерации-это то, что вам действительно нужно, вы можете попробовать LinkedHashMap.

API для Map не гарантирует любой порядок вообще, даже между несколькими вызовами метода на одном и том же объекте.

на практике я был бы очень удивлен, если бы порядок итераций изменился для нескольких последующих вызовов (предполагая, что сама карта не изменилась между ними) - но вы не должны (и в соответствии с API не можете) полагаться на это.

EDIT-если вы хотите полагаться на последовательность порядка итераций, то вы хотите SortedMap который обеспечивает именно эти гарантии.

просто для удовольствия, я решил написать некоторый код, который можно использовать, чтобы гарантировать случайный порядок каждый раз. Это полезно, Так что вы можете поймать случаи, когда вы находитесь в зависимости от заказа, но вы не должны быть. Если вы хотите зависеть от порядка, чем, как говорили другие, вы должны использовать SortedMap. Если вы просто используете карту и полагаетесь на порядок, то с помощью следующего Рандомитератора поймаете это. Я бы использовал его только в тестовом коде, так как он использует больше памяти, а не делает так и будет.

вы также можете обернуть карту (или набор), чтобы они вернули 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();
    }
}

Hashmap не гарантирует, что порядок карты будет оставаться постоянным с течением времени.

это не должно быть. Функция набора ключей карты возвращает набор, и метод итератора набора говорит об этом в своей документации:

"возвращает iterator по элементам в этом наборе. Элементы возвращаются в произвольном порядке (если этот набор не является экземпляром некоторого класса, который обеспечивает гарантию)."

Итак, если вы не используете один из этих классов с гарантией, его нет.

Map-это интерфейс, и он не определяет в документации, что порядок должен быть одинаковым. Это означает, что вы не можете полагаться на порядок. Но если вы управляете реализацией карты, возвращенной getMap (), то вы можете использовать LinkedHashMap или TreeMap и получать один и тот же порядок ключей/значений все время, когда вы повторяете их.

логически, если в контракте сказано "никакой конкретный заказ не гарантирован", а поскольку "заказ вышел один раз" - это определенном порядке, тогда ответ Нет, вы не можете зависеть от того, что он выйдет таким же образом дважды.

Я согласен с LinkedHashMap вещь. Просто положить мои выводы и опыт, когда я столкнулся с проблемой, когда я пытался сортировать 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");

}

у меня есть функция 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() +"] ");

    }

}

теперь, когда я печатаю карту перед сортировкой, она печатает следующую последовательность:

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);

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] 

вы можете увидеть разницу в порядке ключей. Упорядоченный порядок ключей прекрасен, но ключ скопированной карты снова находится в том же порядке, что и предыдущая карта. Я не знаю, правильно ли это сказать, но для двух hashmap с одинаковыми ключами порядок ключей одинаков. Это означает, что порядок ключей не гарантируется, но может быть одинаковым для двух карт с одинаковыми ключами из-за присущий характер алгоритма вставки ключа, если реализация HashMap этой версии JVM.

теперь, когда я использую 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);

выход:

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] 

вы также можете сохранить экземпляр Set, возвращенный методом keySet (), и можете использовать этот экземпляр всякий раз, когда вам нужен тот же порядок.