como obter a única entrada do hashmap sem iterar

172

Existe uma maneira elegante de obter apenas um Entry<K,V>do HashMap, sem iterar, se a chave não for conhecida.

Como a ordem de entrada não é importante, podemos dizer algo como

hashMapObject.get(zeroth_index);

Embora eu esteja ciente de que não existe esse método get by index.

Se eu tentasse a abordagem mencionada abaixo, ainda seria necessário obter todo o conjunto de entradas do hashmap .

for(Map.Entry<String, String> entry : MapObj.entrySet()) {
    return entry;
}

Sugestões são bem vindas.

Edição: Por favor, sugerir qualquer outra estrutura de dados para ser suficiente.

nilesh
fonte

Respostas:

93

A resposta de Jesper é boa. Uma outra solução é usar o TreeMap (você solicitou outras estruturas de dados).

TreeMap<String, String> myMap = new TreeMap<String, String>();
String first = myMap.firstEntry().getValue();
String firstOther = myMap.get(myMap.firstKey());

O TreeMap possui uma sobrecarga, portanto o HashMap é mais rápido, mas apenas como exemplo de uma solução alternativa.

Per Östlund
fonte
Ou ConcurrentSkipListMap
Stephen Denne
Por alguma razão, o firstEntry()método não está definido na interface, SortedMapmas apenas em TreeMape ConcurrentSkipListMap:( #
1100
1
firstEntry () é definido na interface NavigableMap.
Por Östlund
Ah, obrigado, só olhei SortedMapporque tinha o firstKey()método.
Janko
251

Os mapas não são ordenados, portanto não existe a 'primeira entrada', e é também por isso que não existe um método de obtenção por índice em Map(ou HashMap).

Você poderia fazer isso:

Map<String, String> map = ...;  // wherever you get this from

// Get the first entry that the iterator returns
Map.Entry<String, String> entry = map.entrySet().iterator().next();

(Nota: a verificação de um mapa vazio foi omitida).

Seu código não recebe todas as entradas no mapa, ele retorna imediatamente (e sai do loop) com a primeira entrada encontrada.

Para imprimir a chave e o valor desse primeiro elemento:

System.out.println("Key: "+entry.getKey()+", Value: "+entry.getValue());

Nota: A chamada iterator()não significa que você está repetindo o mapa inteiro.

Jesper
fonte
3
Apenas uma observação: LinkedHashMapmantém as chaves na ordem em que foram inseridas.
Matthieu
2
Sim, os mapas em geral não são ordenados, mas algumas Mapimplementações como LinkedHashMape TreeMaptêm uma ordem definida. ( HashMapnão).
Jesper
1
Resposta melhor do que a resposta escolhida - já que a resposta escolhida usa o TreeMap, que espera que a chave seja do tipo Comparável.
Yazad
28

Eu acho que o iterador pode ser a solução mais simples.

return hashMapObject.entrySet().iterator().next();

Outra solução (não bonita):

return new ArrayList(hashMapObject.entrySet()).get(0);

Ou ainda (não melhor):

return hashMapObject.entrySet().toArray()[0];
Cadrian
fonte
7
Não há razão para usar a segunda ou terceira versão. O primeiro está perfeitamente bem, os outros dois perdem muito tempo fornecendo uma matriz / lista para nada.
Joachim Sauer
4
Concordo, portanto, os "não é bonito" comentários ;-)
cadrian
14

Obter valores, convertê-lo em uma matriz, obter o primeiro elemento da matriz:

map.values().toArray()[0]

W.

Wiktor Misiek
fonte
8

Por que você deseja evitar chamá- entrySet()lo geralmente não cria um objeto totalmente novo com seu próprio contexto, mas apenas fornece um objeto de fachada. Simplesmente falar entrySet()é uma operação bem barata.

Joachim Sauer
fonte
8

Se você estiver usando o Java 8, é tão simples quanto findFirst () :

Exemplo rápido:

Optional<Car> theCarFoundOpt = carMap.values().stream().findFirst();

if(theCarFoundOpt.isPresent()) {
    return theCarFoundOpt.get().startEngine();
}
Cacho Santa
fonte
6

Se você realmente deseja a API sugerida, pode subclassificar o HashMap e acompanhar as chaves em uma Lista, por exemplo. Não vejo realmente o ponto disso, mas dá o que você deseja. Se você explicar o caso de uso pretendido, talvez possamos encontrar uma solução melhor.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SuppressWarnings("unchecked")
public class IndexedMap extends HashMap {

    private List<Object> keyIndex;

    public IndexedMap() {
        keyIndex = new ArrayList<Object>();
    }

    /**
     * Returns the key at the specified position in this Map's keyIndex.
     * 
     * @param index
     *            index of the element to return
     * @return the element at the specified position in this list
     * @throws IndexOutOfBoundsException
     *             if the index is out of range (index < 0 || index >= size())
     */
    public Object get(int index) {
        return keyIndex.get(index);
    }

    @Override
    public Object put(Object key, Object value) {

        addKeyToIndex(key);
        return super.put(key, value);
    }

    @Override
    public void putAll(Map source) {

        for (Object key : source.keySet()) {
            addKeyToIndex(key);
        }
        super.putAll(source);
    }

    private void addKeyToIndex(Object key) {

        if (!keyIndex.contains(key)) {
            keyIndex.add(key);
        }
    }

    @Override
    public Object remove(Object key) {

        keyIndex.remove(key);
        return super.remove(key);
    }
}

EDIT: Eu deliberadamente não mergulhei no lado genérico disso ...

Adriaan Koster
fonte
4

O que você quer dizer com "sem iterar"?

Você pode usar map.entrySet().iterator().next()e não itera pelo mapa (no sentido de "tocar em cada objeto"). Você não pode se apossar de um Entry<K, V>sem usar um iterador. O Javadoc do Map.Entry diz:

O método Map.entrySet retorna uma exibição de coleção do mapa, cujos elementos são dessa classe. A única maneira de obter uma referência a uma entrada do mapa é a partir do iterador dessa visão de coleção. Esses objetos Map.Entry são válidos apenas pela duração da iteração.

Você pode explicar com mais detalhes o que você está tentando realizar? Se você deseja manipular objetos primeiro, que correspondam a um critério específico (como "ter uma chave específica") e retornem aos objetos restantes, consulte um PriorityQueue . Ele ordenará seus objetos com base na ordem natural ou em uma definição personalizada Comparatorque você fornecer.

janko
fonte
4
import java.util.*;

public class Friday {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("code", 10);
        map.put("to", 11);
        map.put("joy", 12);

        if (! map.isEmpty()) {
            Map.Entry<String, Integer> entry = map.entrySet().iterator().next();
            System.out.println(entry);
        }
    }
}

Essa abordagem não funciona porque você usou o HashMap. Presumo que o uso do LinkedHashMap seja a solução certa nesse caso.

dmgcodevil
fonte
1
Que método existe no LinkedHashMap que faz o LinkedHashmap funcionar e que esse código não funciona?
SL Barth - Restabelece Monica
3

Isso obteria uma única entrada do mapa, o mais próximo possível, dado que 'first' não se aplica.

import java.util.*;

public class Friday {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<String, Integer>();

        map.put("code", 10);
        map.put("to", 11);
        map.put("joy", 12);

        if (! map.isEmpty()) {
            Map.Entry<String, Integer> entry = map.entrySet().iterator().next();
            System.out.println(entry);
        }
    }
}
Michael Easter
fonte
2

Tropecei aqui procurando a mesma coisa ... Lembrei-me então da Iterablesaula da biblioteca Guava .

Para obter o "primeiro" elemento: Iterables.getFirst( someMap.values(), null );.
Essencialmente, faz o mesmo que Map.values().iterator().next(), mas também permite que você especifique um padrão (neste caso, nulo), caso não exista nada no Mapa.

Iterables.getLast( someMap.values(), null ); retorna o último elemento no mapa.

Iterables.get( someMap.values(), 7, null ); retorna o sétimo elemento no mapa, se existir, caso contrário, um valor padrão (nesse caso, nulo).

Lembre-se, porém, que os HashMaps não são solicitados ... portanto, não espere Iterables.getFirstdevolver o primeiro item que você jogou lá ... da mesma forma Iterables.getLast. Talvez seja útil obter um valor mapeado.

Pode não valer a pena adicionar a biblioteca Guava apenas para isso, mas se você estiver usando alguns dos outros utilitários legais dessa biblioteca ...

SquidRott
fonte
1

Após a sua edição, aqui está a minha sugestão:

Se você tiver apenas uma entrada, poderá substituir o Mapa por um objeto duplo. Dependendo dos tipos e de suas preferências:

  • uma matriz (de 2 valores, chave e valor)
  • um objeto simples com duas propriedades
KLE
fonte
0
map<string,string>m;
auto it=m.begin();//get iterator to the first element of map(m)
return m->first;//return first string(1st string in map<string,string>m)
//Incase you want the second string 
//you can use return m->second(2st string in map<string,string>m)
//if you want to iterate the whole map you can use loop
for(auto it:m)//m is a map
   cout<<it->first<<endl;
Vipul Vikram
fonte
4
Embora esse código possa responder à pergunta, fornecer um contexto adicional a respeito de por que e / ou como esse código responde à pergunta melhora seu valor a longo prazo.
Alex Riabov 5/07
1
Sim, adicione algumas explicações junto com o seu código.
Jkdev 6/07
-3

Eu recebi a resposta: (É simples)

Pegue um ArrayList, depois faça o elenco e encontre o tamanho do arraylist. Aqui está :

    ArrayList count = new ArrayList();
    count=(ArrayList) maptabcolname.get("k1"); //here "k1" is Key
    System.out.println("number of elements="+count.size());

Ele mostrará o tamanho. (Dê uma sugestão). Está funcionando.

Indranil Banerjee
fonte
2
Como isso obtém um item arbitrário de um mapa de hash?
Coronel Trinta e Dois