Java LinkedHashMap получить первую или последнюю запись



я использовал LinkedHashMap потому что важен порядок, в котором ключи вводятся на карте.



но теперь я хочу получить значение ключа в первую очередь (первая запись) или последний.



должен ли быть такой метод, как first() и last() или что-то подобное?



мне нужно иметь итератор, чтобы просто получить первый ключ? Вот почему я использовал LinkedHashMap!



спасибо!

687   12  

12 ответов:

семантика LinkedHashMap все еще те из карты, а не что LinkedList. Он сохраняет порядок вставки, да, но это деталь реализации, а не его интерфейс.

самый быстрый способ получить "первую" запись еще entrySet().iterator().next(). Получение "последней" записи возможно, но повлечет за собой итерацию по всему набору записей путем вызова .next() пока вы не дойдете до последнего. while (iterator.hasNext()) { lastElement = iterator.next() }

edit: однако, если вы готовы выходите за пределы JavaSE API,Apache Commons Collections своя LinkedMap реализация, которая имеет такие методы, как firstKey и lastKey, которые делают то, что вы ищете. Интерфейс значительно богаче.

вы можете попробовать сделать что-то вроде (чтобы получить последнюю запись):

linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];

Это O (N):)

LinkedHashMap текущая реализация (Java 8) отслеживает свой хвост. Если производительность является проблемой и / или карта имеет большой размер, вы можете получить доступ к этому полю через отражение.

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

Это может выглядеть так:

public static <K, V> Entry<K, V> getFirst(Map<K, V> map) {
  if (map.isEmpty()) return null;
  return map.entrySet().iterator().next();
}

public static <K, V> Entry<K, V> getLast(Map<K, V> map) {
  try {
    if (map instanceof LinkedHashMap) return getLastViaReflection(map);
  } catch (Exception ignore) { }
  return getLastByIterating(map);
}

private static <K, V> Entry<K, V> getLastByIterating(Map<K, V> map) {
  Entry<K, V> last = null;
  for (Entry<K, V> e : map.entrySet()) last = e;
  return last;
}

private static <K, V> Entry<K, V> getLastViaReflection(Map<K, V> map) throws NoSuchFieldException, IllegalAccessException {
  Field tail = map.getClass().getDeclaredField("tail");
  tail.setAccessible(true);
  return (Entry<K, V>) tail.get(map);
}

еще один способ получить первую и последнюю запись LinkedHashMap-это использовать метод" toArray " интерфейса Set.

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

использование методов массива приводит к предупреждению формы " ...необходимо непроверенное преобразование, чтобы соответствовать ..." который не может быть исправлен [но может быть подавлен только с помощью аннотации @SuppressWarnings ("unchecked")].

вот небольшой пример, чтобы продемонстрировать использование метода "toArray":

public static void main(final String[] args) {
    final Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>();
    orderMap.put(6, "Six");
    orderMap.put(7, "Seven");
    orderMap.put(3, "Three");
    orderMap.put(100, "Hundered");
    orderMap.put(10, "Ten");

    final Set<Entry<Integer, String>> mapValues = orderMap.entrySet();
    final int maplength = mapValues.size();
    final Entry<Integer,String>[] test = new Entry[maplength];
    mapValues.toArray(test);

    System.out.print("First Key:"+test[0].getKey());
    System.out.println(" First Value:"+test[0].getValue());

    System.out.print("Last Key:"+test[maplength-1].getKey());
    System.out.println(" Last Value:"+test[maplength-1].getValue());
}

// the output geneated is :
First Key:6 First Value:Six
Last Key:10 Last Value:Ten

Я знаю, что я пришел слишком поздно, но я хотел бы предложить некоторые альтернативы, не что-то экстраординарное, но некоторые случаи, которые никто не упоминал здесь. В случае, если кто-то не очень заботится об эффективности, но он хочет что-то с большей простотой(возможно, найти последнее значение записи с одной строкой кода), все это будет довольно упрощено с приходом Java 8 . Я предлагаю несколько полезных сценариев.

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

Возможны Варианты

использование метода массива

Я взял его из предыдущего ответа, чтобы сделать следующие сравнения. Это решение принадлежит @feresr.

  public static String FindLasstEntryWithArrayMethod() {
        return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
    }

использование метода ArrayList

похоже на первое решение с немного другой производительностью

public static String FindLasstEntryWithArrayListMethod() {
        List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
        return entryList.get(entryList.size() - 1).getValue();
    }

Уменьшить Методом

этот метод будет уменьшать набор элементов до получения последнего элемента потока. Кроме того, он будет возвращать только детерминированные результаты

public static String FindLasstEntryWithReduceMethod() {
        return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
    }

Метод SkipFunction

этот метод получит последний элемент потока, просто пропустив все элементы до это

public static String FindLasstEntryWithSkipFunctionMethod() {
        final long count = linkedmap.entrySet().stream().count();
        return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
    }

Альтернативный Интерфейс Iterable

Iterables.getLast от Google Guava. Он имеет некоторую оптимизацию для Списки и сортированные наборы тоже

public static String FindLasstEntryWithGuavaIterable() {
        return Iterables.getLast(linkedmap.entrySet()).getValue();
    }

вот полный исходный код

import com.google.common.collect.Iterables;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public class PerformanceTest {

    private static long startTime;
    private static long endTime;
    private static LinkedHashMap<Integer, String> linkedmap;

    public static void main(String[] args) {
        linkedmap = new LinkedHashMap<Integer, String>();

        linkedmap.put(12, "Chaitanya");
        linkedmap.put(2, "Rahul");
        linkedmap.put(7, "Singh");
        linkedmap.put(49, "Ajeet");
        linkedmap.put(76, "Anuj");

        //call a useless action  so that the caching occurs before the jobs starts.
        linkedmap.entrySet().forEach(x -> {});



        startTime = System.nanoTime();
        FindLasstEntryWithArrayListMethod();
        endTime = System.nanoTime();
        System.out.println("FindLasstEntryWithArrayListMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");


         startTime = System.nanoTime();
        FindLasstEntryWithArrayMethod();
        endTime = System.nanoTime();
        System.out.println("FindLasstEntryWithArrayMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.nanoTime();
        FindLasstEntryWithReduceMethod();
        endTime = System.nanoTime();

        System.out.println("FindLasstEntryWithReduceMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.nanoTime();
        FindLasstEntryWithSkipFunctionMethod();
        endTime = System.nanoTime();

        System.out.println("FindLasstEntryWithSkipFunctionMethod : " + "took " + new BigDecimal((endTime - startTime) / 1000000.000).setScale(3, RoundingMode.CEILING) + " milliseconds");

        startTime = System.currentTimeMillis();
        FindLasstEntryWithGuavaIterable();
        endTime = System.currentTimeMillis();
        System.out.println("FindLasstEntryWithGuavaIterable : " + "took " + (endTime - startTime) + " milliseconds");


    }

    public static String FindLasstEntryWithReduceMethod() {
        return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue();
    }

    public static String FindLasstEntryWithSkipFunctionMethod() {
        final long count = linkedmap.entrySet().stream().count();
        return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue();
    }

    public static String FindLasstEntryWithGuavaIterable() {
        return Iterables.getLast(linkedmap.entrySet()).getValue();
    }

    public static String FindLasstEntryWithArrayListMethod() {
        List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet());
        return entryList.get(entryList.size() - 1).getValue();
    }

    public static String FindLasstEntryWithArrayMethod() {
        return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]);
    }
}

вот вывод с производительностью каждого метода

FindLasstEntryWithArrayListMethod : took 0.162 milliseconds
FindLasstEntryWithArrayMethod : took 0.025 milliseconds
FindLasstEntryWithReduceMethod : took 2.776 milliseconds
FindLasstEntryWithSkipFunctionMethod : took 3.396 milliseconds
FindLasstEntryWithGuavaIterable : took 11 milliseconds

возможно, что-то вроде этого :

LinkedHashMap<Integer, String> myMap;

public String getFirstKey() {
  String out = null;
  for (int key : myMap.keySet()) {
    out = myMap.get(key);
    break;
  }
  return out;
}

public String getLastKey() {
  String out = null;
  for (int key : myMap.keySet()) {
    out = myMap.get(key);
  }
  return out;
}

это немного грязно, но вы можете переопределить removeEldestEntry метод LinkedHashMap, который может подойти вам как частный анонимный член:

private Splat eldest = null;
private LinkedHashMap<Integer, Splat> pastFutures = new LinkedHashMap<Integer, Splat>() {

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Splat> eldest) {

        eldest = eldest.getValue();
        return false;
    }
};

так что вы всегда сможете получить первую запись на вашем eldest - члены. Он будет обновляться каждый раз, когда вы выполняете put.

это также должно быть легко переопределить put и set youngest ...

    @Override
    public Splat put(Integer key, Splat value) {

        youngest = value;
        return super.put(key, value);
    }

все это ломается, когда вы начинаете удалять записи, хотя; не выяснили способ чтобы Клудж это понял.

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

Я бы рекомендовал использовать ConcurrentSkipListMap имеющего firstKey() и lastKey() методы

предложение:

map.remove(map.keySet().iterator().next());

хотя linkedHashMap не предоставляет никакого метода для получения первого, последнего или любого конкретного объекта.

но это довольно тривиально, чтобы получить :

  • Map orderMap = new LinkedHashMap ();
    Set al = orderMap.набор ключей();

теперь с помощью итератора на Al объекта; вы можете получить любой объект.

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

private String getDefaultPlayerType()
{
    String defaultPlayerType = "";
    for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet())
    {
        defaultPlayerType = entry.getKey();
        break;
    }
    return defaultPlayerType;
}

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

вот несколько хороших ответов о том, как изменить порядок hashmap:

как перебрать hashmap в обратном порядке в Java

Если вы используете помощь по ссылке выше, пожалуйста, дайте им-голоса :) Надеюсь, это может помочь кому-то.

правильно, вы должны вручную перечислить набор ключей до конца linkedlist, затем получить запись по ключу и вернуть эту запись.

Comments

    Ничего не найдено.