developer tip

Java HashMap keySet () 반복 순서가 일치합니까?

optionbox 2020. 11. 11. 19:57
반응형

Java HashMap keySet () 반복 순서가 일치합니까?


지도의 keySet () 메서드에서 반환 된 Set이 특정 순서를 보장하지 않는다는 것을 이해합니다.

내 질문은 여러 반복에 걸쳐 동일한 순서를 보장합니까 ? 예를 들면

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

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

...

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

위의 코드에서 맵이 수정 되지 않았다고 가정 하면 keySet에 대한 반복 순서가 동일합니다. 썬의 jdk15를 사용하면 그것은 않습니다 같은 순서로 반복 처리를,하지만이 동작에 의존하기 전에, 나는 모두의 JDK가 같은 행동을 할 것인지 알고 싶습니다.

편집하다

나는 그것에 의지 할 수없는 대답에서 봅니다. 안 됐네요. 주문을 보장하기 위해 새로운 컬렉션을 만들 필요가없는 상황에서 벗어나고 싶었습니다. 내 코드는 반복하고 논리를 수행 한 다음 동일한 순서로 다시 반복해야했습니다. 순서를 보장 할 keySet에서 새로운 ArrayList를 생성합니다.


API 문서에서 보장한다고 명시되어 있지 않은 경우에는 의존해서는 안됩니다. 동작은 동일한 공급 업체의 JDK에서도 JDK의 한 릴리스에서 다음 릴리스로 변경 될 수 있습니다.

당신은 쉽게 세트를 얻은 다음 직접 정렬 할 수 있습니다.


반복 순서가 변경되지 않는 HashMap을 원하는 경우 LinkedHashMap을 사용할 수 있습니다 .

또한 컬렉션을 반복하는 경우 항상 사용해야합니다. HashMap의 entrySet 또는 keySet에 대한 반복은 LinkedHashMap보다 훨씬 느립니다.


Map은 (클래스가 아니라) 인터페이스 일뿐입니다. 즉,이를 구현하는 기본 클래스가 다르게 동작 할 수 있으며 API의 keySet () 계약은 일관된 반복이 필요함을 나타내지 않습니다.

Map을 구현하는 특정 클래스 (HashMap, LinkedHashMap, TreeMap 등)를보고 있다면 소스를 확인하여 동작이 무엇인지 결정하기 위해 keySet () 함수를 구현하는 방법을 볼 수 있습니다. 알고리즘을 자세히 살펴보고 찾고있는 속성이 유지되는지 확인합니다 (즉, 맵에 반복 사이에 삽입 / 제거가없는 경우 일관된 반복 순서). 예를 들어 HashMap의 소스는 다음과 같습니다 (open JDK 6) : http://www.docjar.com/html/api/java/util/HashMap.java.html

JDK마다 크게 다를 수 있으므로 확실히 의존하지 않을 것입니다.

즉, 일관된 반복 순서가 정말로 필요한 경우 LinkedHashMap을 사용해 볼 수 있습니다.


지도의 API는 보장하지 않는 어떠한 경우에도 동일한 개체에 대한 방법의 여러 호출 사이, 어떠한 순서.

실제로는 여러 후속 호출에 대해 반복 순서가 변경되면 매우 놀라 울 것입니다 (맵 자체가 그 사이에 변경되지 않았다고 가정).하지만 API에 따라 이에 의존해서는 안됩니다.

편집-일관된 반복 순서에 의존하려면 정확히 이러한 보장을 제공 하는 SortedMap원합니다 .


재미로 매번 무작위 순서를 보장하는 데 사용할 수있는 코드를 작성하기로 결정했습니다. 이것은 주문에 의존하지만 그렇게해서는 안되는 경우를 포착 할 수 있도록 유용합니다. 다른 사람들이 말한 것보다 순서에 의존하려면 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();
    }
}

Hashmap은지도의 순서가 시간이 지남에 따라 일정하게 유지된다는 것을 보장하지 않습니다.


그럴 필요는 없습니다. 맵의 keySet 함수는 Set을 반환하고 세트의 반복기 메서드는 설명서에서 다음과 같이 말합니다.

"이 세트의 요소에 대한 반복자를 리턴합니다. 요소는 특정 순서없이 리턴됩니다 (이 세트가 보증을 제공하는 일부 클래스의 인스턴스가 아닌 경우)."

따라서 이러한 클래스 중 하나를 보증으로 사용하지 않는 한 아무것도 없습니다.


맵은 인터페이스이며 문서에서 순서가 동일해야한다고 정의하지 않습니다. 즉, 주문에 의존 할 수 없습니다. 그러나 getMap ()에서 반환 된 Map 구현을 제어하는 ​​경우 LinkedHashMap 또는 TreeMap을 사용하여 반복 할 때마다 동일한 순서의 키 / 값을 얻을 수 있습니다.


논리적으로 계약서에 "특정 주문이 보장되지 않는다"고 말하고 "한 번에 나온 주문"이 특정 주문 이기 때문에 대답은 '아니오'입니다. 같은 방식으로 두 번 나오는 것에 의존 할 수 없습니다.


I agree with LinkedHashMap thing. Just putting my findings and experience while I was facing the problem when I was trying to sort HashMap by keys.

My code to create 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");

}

I have a function showMap which prints entries of map:

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

    }

}

Now when I print the map before sorting, it prints following sequence:

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] 

Which is basically different than the order in which map keys were put.

Now When I sort it with map keys:

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

the 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] 

You can see the difference in order of keys. Sorted order of keys is fine but that of keys of copied map is again in the same order of the earlier map. I dont know if this is valid to say, but for two hashmap with same keys, order of keys is same. This implies to the statement that order of keys is not guaranteed but can be same for two maps with same keys because of inherent nature of key insertion algorithm if HashMap implementation of this JVM version.

Now when I use LinkedHashMap to copy sorted Entries to HashMap, I get desired result (which was natural, but that is not the point. Point is regarding order of keys of 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);

Output:

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] 

You also can store the Set instance returned by the keySet() method and can use this instance whenever you need the same order.

참고URL : https://stackoverflow.com/questions/1882762/is-the-java-hashmap-keyset-iteration-order-consistent

반응형