Подтвердить что ты не робот

Является последовательностью итераций Java HashMap()?

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

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

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

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

...

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

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

ИЗМЕНИТЬ

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

4b9b3361

Ответ 1

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

Вы можете легко получить набор, а затем просто отсортировать его самостоятельно, правильно?

Ответ 2

Вы можете использовать LinkedHashMap, если вам нужен HashMap, порядок итераций которого не изменяется.

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

Логически, если в контракте говорится, что "не гарантирован конкретный порядок", а так как "порядок, который он выходил один раз", является определенным порядком, тогда ответ "нет", вы не можете зависеть от него, исходя из того же дважды.

Ответ 10

Я согласен с 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);

выход:

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 этой версии 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] 

Ответ 11

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