162

我之所以使用LinkedHashMap它是因为在地图中输入键的顺序很重要。

但是现在我想首先(第一个输入的条目)或最后一个获取 key 的值。

应该有类似first()andlast()或类似的方法吗?

我是否需要一个迭代器才能获得第一个键条目?这就是我用的原因LinkedHashMap

谢谢!

4

16 回答 16

180

的语义LinkedHashMap仍然是 Map 的语义,而不是 a的语义LinkedList。它保留了插入顺序,是的,但这是一个实现细节,而不是其接口的一个方面。

获得“第一个”条目的最快方法仍然是entrySet().iterator().next(). 获取“最后一个”条目是可能的,但需要通过调用迭代整个条目集,.next()直到到达最后一个。 while (iterator.hasNext()) { lastElement = iterator.next() }

编辑:但是,如果您愿意超越 JavaSE API,Apache Commons Collections有它自己的LinkedMap实现,它有类似firstKeyand的方法,可以满足lastKey您的需求。界面要丰富得多。

于 2009-12-20T17:58:01.457 回答
29

您可以尝试做类似的事情(获取最后一个条目):

linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];
于 2016-04-19T07:25:57.137 回答
23

我知道我来得太晚了,但我想提供一些替代方案,不是什么特别的东西,而是一些这里没有提到的案例。如果有人不太关心效率,但他想要更简单的东西(也许用一行代码找到最后一个入口值),随着 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();
    }

跳过函数方法

此方法将通过简单地跳过它之前的所有元素来获取流的最后一个元素

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

可迭代替代

来自 Google Guava 的 Iterables.getLast。它也对 Lists 和 SortedSets 进行了一些优化

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
于 2018-04-16T14:38:08.487 回答
13

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);
}
于 2015-11-27T15:05:09.957 回答
6

获取 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
于 2009-12-21T05:12:19.877 回答
5

这有点脏,但是您可以覆盖removeEldestEntryLinkedHashMap 的方法,它可能适合您作为私有匿名成员执行:

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和设置youngest......

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

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

但是,当您开始删除条目时,一切都会崩溃;还没有想出办法来解决这个问题。

非常烦人的是,您无法以合理的方式访问 head 或 tail ......

于 2016-02-09T17:46:26.717 回答
4

也许是这样的:

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;
}
于 2013-03-22T00:03:42.023 回答
2

建议:

map.remove(map.keySet().iterator().next());
于 2016-03-31T15:27:41.580 回答
1

我建议使用具有和方法的ConcurrentSkipListMapfirstKey()lastKey()

于 2012-09-19T22:43:55.977 回答
1

尽管linkedHashMap 没有提供任何方法来获取第一个、最后一个或任何特定对象。

但它非常微不足道:

Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>();  
Set<Integer> al =   orderMap.keySet();

al现在在对象上使用迭代器;你可以得到任何对象。

于 2012-11-07T07:14:42.287 回答
1

对于第一个元素使用entrySet().iterator().next()并在 1 次迭代后停止迭代。对于最后一种,最简单的方法是在执行 map.put 时将键保留在变量中。

于 2020-07-01T10:18:06.930 回答
1
        import java.util.Arrays;
        import java.util.LinkedHashMap;
        import java.util.List;
        import java.util.Map;

        public class Scratch {
           public static void main(String[] args) {

              // Plain java version

              Map<String, List<Integer>> linked = new LinkedHashMap<>();
              linked.put("a", Arrays.asList(1, 2, 3));
              linked.put("aa", Arrays.asList(1, 2, 3, 4));
              linked.put("b", Arrays.asList(1, 2, 3, 4, 5));
              linked.put("bb", Arrays.asList(1, 2, 3, 4, 5, 6));

              System.out.println("linked = " + linked);

              String firstKey = getFirstKey(linked);
              System.out.println("firstKey = " + firstKey);
              List<Integer> firstEntry = linked.get(firstKey);
              System.out.println("firstEntry = " + firstEntry);

              String lastKey = getLastKey(linked);
              System.out.println("lastKey = " + lastKey);
              List<Integer> lastEntry = linked.get(lastKey);
              System.out.println("lastEntry = " + lastEntry);



           }

           private static String getLastKey(Map<String, List<Integer>> linked) {
              int index = 0;
              for (String key : linked.keySet()) {
             index++;
             if (index == linked.size()) {
                return key;
             }
              }
              return null;
           }

           private static String getFirstKey(Map<String, List<Integer>> linked) {
              for (String key : linked.keySet()) {
             return key;
              }
              return null;
           }
        }
于 2020-11-10T08:16:48.420 回答
1

使用Java8 流,这可以很容易地完成:

LinkedHashMap<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("A", 1);
linkedHashMap.put("B", 2);
linkedHashMap.put("C", 3);
linkedHashMap.put("D", 4);

//First entry
Map.Entry<String, Integer> firstEntry = linkedHashMap.entrySet().stream().findFirst().get();

//Last entry
Map.Entry<String, Integer> lastEntry = linkedHashMap.entrySet().stream().skip(linkedHashMap.size() - 1).findFirst().get();
于 2022-01-07T16:48:53.870 回答
0

是的,我遇到了同样的问题,但幸运的是我只需要第一个元素...... - 这就是我为它所做的。

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

如果您还需要最后一个元素-我会研究如何反转地图的顺序-将其存储在临时变量中,访问反转地图中的第一个元素(因此它将是您的最后一个元素),杀死临时变量。

以下是关于如何反转哈希图的一些很好的答案:

如何在Java中以相反的顺序迭代hashmap

如果您使用上述链接的帮助,请给他们投票 :) 希望这可以帮助某人。

于 2013-07-26T15:04:56.707 回答
0

对,您必须手动枚举键集直到链表的末尾,然后通过键检索条目并返回该条目。

于 2018-08-18T05:30:14.133 回答
0
public static List<Fragment> pullToBackStack() {
    List<Fragment> fragments = new ArrayList<>();
    List<Map.Entry<String, Fragment>> entryList = new ArrayList<>(backMap.entrySet());
    int size = entryList.size();
    if (size > 0) {
        for (int i = size - 1; i >= 0; i--) {// last Fragments
            fragments.add(entryList.get(i).getValue());
            backMap.remove(entryList.get(i).getKey());
        }
        return fragments;
    }
    return null;
}
于 2020-05-17T20:12:30.453 回答