Pergunta

Entendo que o conjunto retornou do método KeySet () de um mapa não garante nenhum pedido específico.

Minha pergunta é: garante o mesmo encomendar por várias iterações. Por exemplo

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

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

...

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

No código acima, assumindo que o mapa é não Modificado, a iteração sobre os teclas estará na mesma ordem. Usando o JDK15 do Sun faz itera na mesma ordem, mas antes de depender desse comportamento, gostaria de saber se todos os JDKs farão o mesmo.

EDITAR

Vejo pelas respostas que não posso depender disso. Que pena. Eu esperava me safar de não ter que criar uma nova coleção para garantir meu pedido. Meu código precisava iterar, fazer alguma lógica e depois iterar novamente com a mesma ordem. Vou apenas criar um novo Arraylist a partir do Keyset, que garantirá o pedido.

Foi útil?

Solução

Se não for declarado garantido na documentação da API, você não deverá depender disso. O comportamento pode até mudar de uma liberação do JDK para o outro, mesmo do mesmo JDK do mesmo fornecedor.

Você poderia facilmente obter o conjunto e depois classificá -lo, certo?

Outras dicas

Você pode usar um LinkedHashmap Se você deseja um hashmap cuja ordem de iteração não muda.

Além disso, você deve sempre usá -lo se iterar através da coleção. O iteração do conjunto de entrada ou do Keyset do Hashmap é muito mais lento do que o LinkedHashmap.

O MAP é apenas uma interface (em vez de uma classe), o que significa que a classe subjacente que a implementa (e há muitos) pode se comportar de maneira diferente, e o contrato para KeySet () na API não indica que a iteração consistente é necessária.

Se você está olhando para uma classe específica que implementa mapa (hashmap, linkedhashmap, Treemap etc.), você pode ver como ele implementa a função KeySet () para determinar qual seria o comportamento verificando a fonte, você teria que Realmente dê uma olhada no algoritmo para ver se a propriedade que você está procurando é preservada (ou seja, ordem de iteração consistente quando o mapa não teve nenhuma inserção/remoção entre as iterações). A fonte do Hashmap, por exemplo, está aqui (aberto JDK 6): http://www.docjar.com/html/api/java/util/hashmap.java.html

Isso poderia variar amplamente de um JDK para o outro, então eu definitivamente não confiaria nisso.

Dito isto, se a ordem de iteração consistente for algo que você realmente precisa, convém experimentar um LinkedHashmap.

A API para mapa não garante algum ordenando qualquer coisa, mesmo entre múltiplas invocações do método no mesmo objeto.

Na prática, ficaria muito surpreso se a ordem de iteração mudasse para várias invocações subsequentes (assumindo que o próprio mapa não mudasse no meio) - mas você não deve (e de acordo com a API não pode) confiar nisso.

Editar - se você deseja confiar na ordem de iteração ser consistente, então você quer um SortEdmap que fornece exatamente essas garantias.

Apenas por diversão, decidi escrever algum código que você pode usar para garantir uma ordem aleatória a cada vez. Isso é útil para que você possa capturar casos em que está dependendo do pedido, mas não deve ser. Se você deseja depender do pedido, do que os outros disseram, você deve usar um mapa classificado. Se você apenas usar um mapa e confiar no pedido, o uso do seguinte Randomiterator o captura. Eu só o usaria no código de teste, pois faz uso de mais memória do que não fazê -lo.

Você também pode embrulhar o mapa (ou o conjunto) para que eles retornem o RandomeIterator, o que permitiria que você use o loop for-cada.

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

O hashmap não garante que a ordem do mapa permaneça constante ao longo do tempo.

Não precisa ser. A função de tecla de um mapa retorna um conjunto e o método ITERator do conjunto diz isso em sua documentação:

"Retorna um iterador sobre os elementos neste conjunto. Os elementos são retornados em nenhuma ordem específica (a menos que este conjunto seja uma instância de alguma classe que fornece uma garantia)".

Portanto, a menos que você esteja usando uma dessas classes com garantia, não há nenhuma.

O mapa é uma interface e não define na documentação que a ordem deve ser a mesma. Isso significa que você não pode confiar no pedido. Mas se você controlar a implementação do mapa devolvida pelo getMap (), poderá usar o LinkedHashmap ou o TreeMap e obter a mesma ordem de chaves/valores o tempo todo, você o itera através deles.

Logicamente, se o contrato diz "nenhuma ordem específica é garantida" e, como "a ordem que saiu uma vez" é um ordem particular, então a resposta é não, você não pode depender dela saindo da mesma maneira duas vezes.

Eu concordo com a coisa do LinkedHashmap. Apenas colocando minhas descobertas e experiência enquanto eu estava enfrentando o problema quando estava tentando classificar o hashmap por chaves.

Meu código para criar 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");

}

Eu tenho um showmap de função que imprime entradas do mapa:

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

    }

}

Agora, quando imprimo o mapa antes de classificar, ele imprime a sequência seguinte:

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] 

Que é basicamente diferente da ordem em que as teclas do mapa foram colocadas.

Agora, quando eu resolvo as teclas do mapa:

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

A colocação é:

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] 

Você pode ver a diferença em ordem das chaves. A ordem classificada das chaves é boa, mas a das chaves do mapa copiada está novamente na mesma ordem do mapa anterior. Não sei se isso é válido para dizer, mas para dois hashmap com as mesmas chaves, a ordem das chaves é a mesma. Isso implica na afirmação de que a ordem das chaves não é garantida, mas pode ser a mesma para dois mapas com as mesmas chaves devido à natureza inerente ao algoritmo de inserção -chave se a implementação do HashMap desta versão da JVM.

Agora, quando uso o LinkedHashmap para copiar entradas classificadas para o Hashmap, sou o resultado desejado (o que era natural, mas esse não é o ponto. O ponto é sobre a ordem das chaves do 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);

Resultado:

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] 

Você também pode armazenar a instância do set retornada pelo método KeySet () e pode usar esta instância sempre que precisar do mesmo pedido.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top