cómo obtener una entrada de hashmap sin iterar

172

¿Existe una manera elegante de obtener solo uno Entry<K,V>de HashMap, sin iterar, si no se conoce la clave?

Como el orden de entrada no es importante, ¿podemos decir algo como

hashMapObject.get(zeroth_index);

Aunque soy consciente de que no existe tal método get by index.

Si intenté el enfoque mencionado a continuación, aún tendría que obtener todo el conjunto de entradas del hashmap .

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

Las sugerencias son bienvenidas.

EDITAR: sugiera cualquier otra estructura de datos para satisfacer el requisito.

nilesh
fuente

Respuestas:

93

La respuesta de Jesper es buena. Otra solución es usar TreeMap (solicitó otras estructuras de datos).

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

TreeMap tiene una sobrecarga, por lo que HashMap es más rápido, pero solo como un ejemplo de una solución alternativa.

Per Östlund
fuente
O ConcurrentSkipListMap
Stephen Denne
Por alguna razón, el firstEntry()método no está definido en la interfaz SortedMapsino solo en TreeMapy ConcurrentSkipListMap:(
janko
1
firstEntry () se define en la interfaz NavigableMap.
Por Östlund el
Ah, gracias, solo miré SortedMapporque tenía el firstKey()método.
Janko
251

Los mapas no están ordenados, por lo que no existe tal cosa como 'la primera entrada', y esa es también la razón por la cual no hay un método de obtención de índice en Map(o HashMap).

Podrías hacer esto:

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: se omite la comprobación de un mapa vacío).

Su código no obtiene todas las entradas en el mapa, regresa inmediatamente (y se rompe del bucle) con la primera entrada que se encuentra.

Para imprimir la clave y el valor de este primer elemento:

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

Nota: Llamar iterator()no significa que esté iterando sobre todo el mapa.

Jesper
fuente
3
Solo una nota al margen: LinkedHashMapmantiene las claves en el orden en que se insertaron.
Matthieu
2
Sí, los mapas en general no están ordenados, pero algunas Mapimplementaciones como LinkedHashMapy TreeMaptienen un orden definido. ( HashMapno)
Jesper
1
Mejor respuesta que la respuesta elegida, ya que la respuesta elegida usa TreeMap, que espera que la clave sea de tipo Comparable.
Yazad
28

Supongo que el iterador puede ser la solución más simple.

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

Otra solución (no bonita):

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

O aún (no mejor):

return hashMapObject.entrySet().toArray()[0];
cadrian
fuente
77
No hay razón para usar la segunda o tercera versión. El primero está perfectamente bien, los otros dos pierden mucho tiempo al proporcionar una matriz / Lista para nada.
Joachim Sauer
44
Estoy de acuerdo, de ahí los comentarios "no bonitos" ;-)
cadrian
14

Obtenga valores, conviértalo en una matriz, obtenga el primer elemento de la matriz:

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

W.

Wiktor Misiek
fuente
8

Por qué desea evitar llamarlo entrySet(), generalmente no crea un objeto completamente nuevo con su propio contexto, sino que simplemente proporciona un objeto de fachada. Simplemente hablando entrySet()es una operación bastante barata.

Joachim Sauer
fuente
8

Si está utilizando Java 8, es tan simple como findFirst () :

Ejemplo rápido:

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

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

Si realmente desea la API que sugirió, puede subclasificar HashMap y realizar un seguimiento de las claves en una Lista, por ejemplo. Realmente no veo el punto en esto, pero te da lo que quieres. Si explica el caso de uso previsto, tal vez podamos encontrar una mejor solución.

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

EDITAR: deliberadamente no profundicé en el lado genérico de esto ...

Adriaan Koster
fuente
4

¿Qué quieres decir con "sin iterar"?

Puede usar map.entrySet().iterator().next()y no iterar a través del mapa (en el sentido de "tocar cada objeto"). Entry<K, V>Sin embargo, no puedes hacerte con un sin usar un iterador. El Javadoc de Map.Entry dice:

El método Map.entrySet devuelve una vista de colección del mapa, cuyos elementos son de esta clase. La única forma de obtener una referencia a una entrada de mapa es desde el iterador de esta vista de colección. Estos objetos Map.Entry son válidos solo durante la duración de la iteración.

¿Puede explicar con más detalle lo que está tratando de lograr? Si primero desea manejar objetos, que coinciden con un criterio específico (como "tener una clave en particular") y recurrir a los objetos restantes, de lo contrario, mire un PriorityQueue . Ordena tus objetos en función del orden natural o de una definición personalizada Comparatorque proporciones.

janko
fuente
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);
        }
    }
}

Este enfoque no funciona porque usaste HashMap. Supongo que usar LinkedHashMap será la solución correcta en este caso.

dmgcodevil
fuente
1
¿Qué método hay en LinkedHashMap que hace que LinkedHashmap funcione, que este código no funciona?
SL Barth - Restablece a Monica el
3

Esto obtendría una sola entrada del mapa, que tan cerca como uno puede estar, dado que 'primero' realmente no 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
fuente
2

Tropecé aquí buscando lo mismo ... Entonces recordé la Iterablesclase de la biblioteca de Guava .

Para obtener el elemento de "primera": Iterables.getFirst( someMap.values(), null );.
Esencialmente hace lo mismo que Map.values().iterator().next(), pero también le permite especificar un valor predeterminado (en este caso nulo) en caso de que no haya nada en el Mapa.

Iterables.getLast( someMap.values(), null ); devuelve el último elemento en el mapa.

Iterables.get( someMap.values(), 7, null ); devuelve el séptimo elemento en el Mapa si existe, de lo contrario, un valor predeterminado (en este caso, nulo).

Sin embargo, recuerde que los HashMaps no están ordenados ... así que no espere Iterables.getFirstdevolver el primer artículo que arrojó allí ... del mismo modo con Iterables.getLast. Sin embargo, quizás sea útil para obtener un valor mapeado.

Puede que no valga la pena agregar la biblioteca de Guava solo por eso, pero si está utilizando algunas de las otras utilidades geniales dentro de esa biblioteca ...

SquidRott
fuente
1

Después de su EDITAR, aquí está mi sugerencia:

Si solo tiene una entrada, puede reemplazar el Mapa por un objeto dual. Dependiendo de los tipos y sus preferencias:

  • una matriz (de 2 valores, clave y valor)
  • un objeto simple con dos propiedades
KLE
fuente
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
fuente
44
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo responde a la pregunta mejora su valor a largo plazo.
Alex Riabov
1
Sí, agregue alguna explicación junto con su código.
jkdev
-3

Tengo la respuesta: (es simple)

Tome una ArrayList, luego envíela y encuentre el tamaño de la matriz. Aquí está :

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

Mostrará el tamaño. (Dar sugerencia). Esta funcionando.

Indranil Banerjee
fuente
2
¿Cómo obtiene esto un elemento arbitrario de un mapa hash?
Coronel Treinta y Dos