как получить одну запись из hashmap без итерации


172

Существует ли элегантный способ получения только одного Entry<K,V>из HashMap, без итераций, если ключ не известен.

Так как порядок ввода записи не важен, можем ли мы сказать что-то вроде

hashMapObject.get(zeroth_index);

Хотя я знаю, что такого метода индекса не существует.

Если бы я попробовал подход, упомянутый ниже, он все равно должен был бы получить весь набор записей хэш-карты .

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

Предложения приветствуются.

РЕДАКТИРОВАТЬ: Пожалуйста, предложите любую другую структуру данных, чтобы удовлетворить требования.

Ответы:


93

Ответ Джеспера хорош. Другим решением является использование TreeMap (вы просили другие структуры данных).

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

TreeMap имеет накладные расходы, поэтому HashMap работает быстрее, но как пример альтернативного решения.


Или ConcurrentSkipListMap
Стивен Денн,

По какой-то причине firstEntry()метод не определен в интерфейсе, SortedMapа только в TreeMapand ConcurrentSkipListMap:(
janko

1
firstEntry () определяется в интерфейсе NavigableMap.
Per Östlund

Ах, спасибо, я только посмотрел, SortedMapпотому что у него был firstKey()метод.
Янко

251

Карты не упорядочены, поэтому нет такой вещи, как «первая запись», и поэтому также нет метода get-by-index для Map(или HashMap).

Вы могли бы сделать это:

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

(Примечание: проверка на пустую карту опущена).

Ваш код не получает все записи на карте, он сразу же возвращается (и выходит из цикла) с первой найденной записью.

Чтобы напечатать ключ и значение этого первого элемента:

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

Примечание. Вызов iterator()не означает, что вы выполняете итерацию по всей карте.


3
Просто примечание: LinkedHashMapдержит ключи в том порядке, в котором они были вставлены.
Матье

2
Да, карты в общем случае не упорядочены, но некоторые Mapреализации, такие как LinkedHashMapи TreeMapимеют определенный порядок. ( HashMapнет).
Джеспер

1
Лучший ответ, чем выбранный ответ - так как выбранный ответ использует TreeMap, который ожидает, что ключ имеет тип Comparable.
Язад

28

Я думаю, что итератор может быть самым простым решением.

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

Другое решение (не симпатичное):

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

Или пока (не лучше)

return hashMapObject.entrySet().toArray()[0];

7
Нет причин использовать вторую или третью версию. Первый из них отлично подходит, два других тратят много времени, предоставляя массив / список для ничего.
Йоахим Зауэр

4
Я согласен, отсюда и «не красивые» комментарии ;-)
Кадриан

14

Получить значения, преобразовать его в массив, получить первый элемент массива:

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

W.


8

Почему вы хотите избежать entrySet()его вызова, как правило, не создает совершенно новый объект со своим собственным контекстом, а просто предоставляет объект фасада. Проще говоря entrySet(), это довольно дешевая операция.


8

Если вы используете Java 8, это так же просто, как findFirst () :

Быстрый пример:

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

if(theCarFoundOpt.isPresent()) {
    return theCarFoundOpt.get().startEngine();
}

6

Если вы действительно хотите API, который вы предложили, вы можете создать подкласс HashMap и, например, отслеживать ключи в списке. Не вижу смысла в этом на самом деле, но это дает вам то, что вы хотите. Если вы объясните предполагаемый вариант использования, возможно, мы сможем найти лучшее решение.

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

РЕДАКТИРОВАТЬ: я сознательно не углубился в сторону дженериков этого ...


4

Что вы имеете в виду с "без итерации"?

Вы можете использовать, map.entrySet().iterator().next()и вы не будете перебирать карту (в смысле «прикосновение к каждому объекту»). Вы не можете получить Entry<K, V>без использования итератора, хотя. Javadoc of Map.Entry говорит:

Метод Map.entrySet возвращает коллекционное представление карты, элементы которой принадлежат этому классу. Единственный способ получить ссылку на запись на карте - использовать итератор этого коллекционного представления. Эти объекты Map.Entry действительны только в течение итерации.

Можете ли вы объяснить более подробно, что вы пытаетесь достичь? Если вы хотите сначала обработать объекты, которые соответствуют определенному критерию (например, «иметь определенный ключ») и в противном случае отступить к остальным объектам, тогда посмотрите на PriorityQueue . Он упорядочит ваши объекты в соответствии с естественным порядком или заданным Comparatorвами пользователем.


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

Этот подход не работает, потому что вы использовали HashMap. Я предполагаю, что использование LinkedHashMap будет правильным решением в этом случае.


1
Какой метод есть в LinkedHashMap, который заставляет LinkedHashmap работать, а этот код - нет?
SL Barth - Восстановить Монику

3

Это получило бы единственную запись с карты, которая примерно настолько близка, насколько это возможно, учитывая, что «первое» на самом деле не применимо.

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

2

Наткнулся здесь, ища то же самое ... Затем я вспомнил Iterablesкласс из библиотеки Гуавы .

Для того, чтобы получить «первый» элемент: Iterables.getFirst( someMap.values(), null );.
По сути, делает то же, что и Map.values().iterator().next(), но также позволяет вам указать значение по умолчанию (в данном случае ноль), если на карте ничего не будет.

Iterables.getLast( someMap.values(), null ); возвращает последний элемент на карте.

Iterables.get( someMap.values(), 7, null ); возвращает седьмой элемент на карте, если он существует, в противном случае - значение по умолчанию (в данном случае ноль).

Помните, однако, что HashMaps не заказаны ... поэтому не ожидайте Iterables.getFirstвозврата первого элемента, который вы там бросили ... аналогично с Iterables.getLast. Может быть полезно , чтобы получить более отображенное значение , хотя.

Возможно, не стоит добавлять библиотеку Guava только для этого, но если вы используете некоторые другие классные утилиты в этой библиотеке ...


1

После вашего редактирования, вот мое предложение:

Если у вас есть только одна запись, вы можете заменить карту на двойной объект. В зависимости от типов и ваших предпочтений:

  • массив (из 2 значений, ключ и значение)
  • простой объект с двумя свойствами

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;

4
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, повышает его долгосрочную ценность.
Алекс Рябов

1
Да, пожалуйста, добавьте некоторые объяснения вместе с вашим кодом.
jkdev

-3

Я получил ответ: (это просто)

Возьмите ArrayList, затем разыграйте его и найдите размер массива. Вот :

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

Это покажет размер. (Дайте предложение). Это работает.


2
Как это получить один произвольный элемент из хэш-карты?
полковник тридцать два
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.