jak uzyskać jeden wpis z hashmap bez iteracji

172

Czy istnieje elegancki sposób uzyskania tylko jednego Entry<K,V>z HashMap, bez iteracji, jeśli klucz nie jest znany.

Ponieważ kolejność wpisów nie jest ważna, możemy powiedzieć coś takiego

hashMapObject.get(zeroth_index);

Chociaż mam świadomość, że nie ma takiej metody get by index.

Gdybym spróbował podejścia wspomnianego poniżej, nadal musiałby uzyskać cały zestaw wejściowy hasmapy .

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

Sugestie są mile widziane.

EDYCJA: Proszę zaproponować inną strukturę danych, aby była wystarczająca.

nilesh
źródło

Odpowiedzi:

93

Odpowiedź Jespera jest dobra. Innym rozwiązaniem jest użycie TreeMap (poprosiłeś o inne struktury danych).

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

TreeMap ma narzut, więc HashMap jest szybszy, ale tylko jako przykład alternatywnego rozwiązania.

Per Östlund
źródło
Lub ConcurrentSkipListMap
Stephen Denne
Z jakiegoś powodu firstEntry()metoda nie jest zdefiniowana w interfejsie, SortedMapale tylko w TreeMapand ConcurrentSkipListMap:(
janko
1
Metoda firstEntry () jest zdefiniowana w interfejsie NavigableMap.
Per Östlund
Ach, dzięki, patrzyłem tylko SortedMapdlatego, że miał firstKey()metodę.
janko
251

Mapy nie są uporządkowane, więc nie ma czegoś takiego jak „pierwszy wpis” i dlatego też nie ma metody get-by-index na Map(lub HashMap).

Możesz to zrobić:

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

(Uwaga: pominięto sprawdzanie pustej mapy).

Twój kod nie pobiera wszystkich wpisów na mapie, zwraca natychmiast (i przerywa pętlę) z pierwszym znalezionym wpisem.

Aby wydrukować klucz i wartość tego pierwszego elementu:

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

Uwaga: wywołanie iterator()nie oznacza, że ​​iterujesz po całej mapie.

Jesper
źródło
3
Na marginesie: LinkedHashMaputrzymuje klucze w kolejności, w jakiej zostały włożone.
Matthieu
2
Tak, mapy generalnie nie są uporządkowane, ale niektóre Mapimplementacje, takie jak LinkedHashMapi TreeMapmają zdefiniowaną kolejność. ( HashMapnie).
Jesper
1
Lepsza odpowiedź niż wybrana odpowiedź - ponieważ wybrana odpowiedź wykorzystuje TreeMap, która oczekuje, że klucz będzie typu porównywalnego.
Yazad,
28

Myślę, że iterator może być najprostszym rozwiązaniem.

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

Inne rozwiązanie (niezbyt ładne):

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

Albo jeszcze (nie lepiej):

return hashMapObject.entrySet().toArray()[0];
cadrian
źródło
7
Nie ma powodu, aby używać drugiej lub trzeciej wersji. Pierwsza jest w porządku, pozostałe dwie marnują dużo czasu, udostępniając tablicę / listę za nic.
Joachim Sauer
4
Zgadzam się, stąd "
nieładne
14

Pobierz wartości, przekonwertuj je na tablicę, pobierz pierwszy element tablicy:

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

W.

Wiktor Misiek
źródło
8

Dlaczego chcesz uniknąć nazywania entrySet()go generalnie nie tworzy całkowicie nowego obiektu z własnym kontekstem, ale zamiast tego po prostu zapewnia obiekt fasady. Mówiąc najprościej, entrySet()jest to dość tania operacja.

Joachim Sauer
źródło
8

Jeśli korzystasz z Java 8, jest to tak proste, jak findFirst () :

Szybki przykład:

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

if(theCarFoundOpt.isPresent()) {
    return theCarFoundOpt.get().startEngine();
}
Cacho Santa
źródło
6

Jeśli naprawdę chcesz zaproponowanego interfejsu API, możesz na przykład podklasę HashMap i śledzić klucze na liście. Nie widzisz w tym sensu, ale daje ci to, czego chcesz. Jeśli wyjaśnisz zamierzony przypadek użycia, być może znajdziemy lepsze rozwiązanie.

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

EDYCJA: celowo nie zagłębiłem się w ogólną stronę tego ...

Adriaan Koster
źródło
4

Co masz na myśli mówiąc „bez iteracji”?

Możesz używać map.entrySet().iterator().next()i nie będziesz iterować po mapie (w znaczeniu „dotykania każdego obiektu”). Nie możesz jednak zdobyć tego Entry<K, V>bez użycia iteratora. Javadoc z Map.Entry mówi:

Metoda Map.entrySet zwraca widok kolekcji mapy, której elementy należą do tej klasy. Jedynym sposobem uzyskania odwołania do wpisu mapy jest użycie iteratora tego widoku kolekcji. Te obiekty Map.Entry są ważne tylko przez czas trwania iteracji.

Czy możesz wyjaśnić bardziej szczegółowo, co próbujesz osiągnąć? Jeśli chcesz najpierw obsłużyć obiekty, które pasują do określonego kryterium (np. „Mieć określony klucz”), aw przeciwnym razie wrócić do pozostałych obiektów, spójrz na PriorityQueue . Porządkuje Twoje obiekty w oparciu o naturalną kolejność lub niestandardową definicję Comparator, którą podasz.

janko
źródło
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);
        }
    }
}

To podejście nie działa, ponieważ użyłeś HashMap. Zakładam, że użycie LinkedHashMap będzie w tym przypadku właściwym rozwiązaniem.

dmgcodevil
źródło
1
Jaka metoda jest dostępna w LinkedHashMap, która sprawia, że ​​LinkedHashmap działa, a której ten kod nie działa?
SL Barth - Przywróć Monikę
3

Pozwoliłoby to uzyskać pojedynczy wpis z mapy, który jest tak blisko, jak można, biorąc pod uwagę „pierwszy”, tak naprawdę nie ma zastosowania.

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);
        }
    }
}
Michała Wielkanoc
źródło
2

Natknąłem się tutaj, szukając tego samego ... Wtedy przypomniałem sobie Iterablesklasę z biblioteki Guava .

Aby uzyskać „pierwszy” elementu: Iterables.getFirst( someMap.values(), null );.
Zasadniczo robi to samo co Map.values().iterator().next(), ale pozwala również określić wartość domyślną (w tym przypadku wartość null), jeśli na mapie nie ma nic.

Iterables.getLast( someMap.values(), null ); zwraca ostatni element w Map.

Iterables.get( someMap.values(), 7, null ); zwraca 7. element w Map, jeśli istnieje, w przeciwnym razie wartość domyślną (w tym przypadku null).

Pamiętaj jednak, że HashMapy nie są zamawiane ... więc nie spodziewaj Iterables.getFirstsię, że zwrócisz pierwszy przedmiot, który tam wrzuciłeś ... podobnie Iterables.getLast. Być może warto się z odwzorowaną jakość, choć.

Może nie warto dodawać biblioteki Guava tylko w tym celu, ale jeśli zdarzy ci się używać innych fajnych narzędzi w tej bibliotece ...

SquidRott
źródło
1

Po twojej EDYCJI, oto moja sugestia:

Jeśli masz tylko jeden wpis, możesz zastąpić Mapę podwójnym obiektem. W zależności od rodzaju i Twoich preferencji:

  • tablica (2 wartości, klucz i wartość)
  • prosty obiekt z dwiema właściwościami
KLE
źródło
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
źródło
4
Chociaż ten kod może odpowiedzieć na pytanie, zapewnia dodatkowy kontekst dotyczący tego, dlaczego i / lub jak ten kod odpowiada, poprawia jego długoterminową wartość.
Alex Riabov,
1
Tak, proszę dodać wyjaśnienie wraz z kodem.
jkdev
-3

Mam odpowiedź: (to proste)

Weź ArrayList, a następnie rzuć i znajdź rozmiar arraylist. Oto ona:

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

Pokaże rozmiar. (Podaj sugestię). To działa.

Indranil Banerjee
źródło
2
Jak to uzyskuje jeden dowolny element z mapy skrótów?
pułkownik trzydzieści dwa