web-dev-qa-db-ja.com

Java LinkedHashMapは最初または最後のエントリを取得します

LinkedHashMap を使用しました。キーがマップに入力される順序が重要だからです。

しかし今、私は最初の場所(最初に入力されたエントリ)または最後のキーの値を取得したいです。

first()last()などのメソッドがあるべきですか?

最初のキーエントリを取得するためにイテレータが必要ですか? LinkedHashMap !を使用した理由です!

ありがとう!

117
maiky

LinkedHashMapのセマンティクスは、依然としてLinkedListのセマンティクスではなく、Mapのセマンティクスです。はい、挿入順序は保持されますが、それはインターフェースの側面ではなく、実装の詳細です。

「最初の」エントリを取得する最も簡単な方法は、まだentrySet().iterator().next()です。 「最後の」エントリを取得することは可能ですが、最後に到達するまで.next()を呼び出してエントリセット全体を反復処理する必要があります。 while (iterator.hasNext()) { lastElement = iterator.next() }

edit:ただし、JavaSE APIを超えたい場合は、 Apache Commons Collections は独自の LinkedMap 実装には、 firstKey および lastKey などのメソッドがあります。あなたが探していることをしてください。インターフェースはかなり充実しています。

139
skaffman

次のようなことを試してみてください(最後のエントリを取得するため):

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

それはO(N)です:)

19
FRR

私は遅すぎることを知っていますが、特別なものではなく、ここで言及されていないいくつかのケースを提供したいと思います。誰かが効率をあまり気にしていないが、もっとシンプルなものを望んでいる場合(おそらく、1行のコードで最後のエントリ値を見つける)、これはすべて 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();
    }

反復可能な代替

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
11

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

LinkedHashMapの最初と最後のエントリを取得するもう1つの方法は、Setインターフェイスの「toArray」メソッドを使用することです。

ただし、エントリセット内のエントリを繰り返し処理し、最初と最後のエントリを取得する方が適切なアプローチだと思います。

配列メソッドの使用は、フォームの警告につながります 「...に準拠するには、未チェックの変換が必要です...」 これは修正できません[ただし、@ 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
 </ code>
6
sateesh

少し汚れていますが、LinkedHashMapのremoveEldestEntryメソッドをオーバーライドできます。これは、プライベートな匿名メンバーとして行うのに適している場合があります。

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

ただし、エントリの削除を開始すると、すべてが壊れます。それをうまく処理する方法を考え出していない。

それ以外の場合は賢明な方法で頭や尾にアクセスすることはできないことは非常に迷惑です...

3
robert

おそらくこのようなもの:

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;
}
2
user2127649

firstKey()メソッドとlastKey()メソッドを持つ ConcurrentSkipListMap の使用をお勧めします

1
Doua Beri

提案:

map.remove(map.keySet().iterator().next());
1
Tadeu Jr.

正しい場合は、linkedlistの最後までキーセットを手動で列挙してから、キーでエントリを取得し、このエントリを返す必要があります。

0
unknownwill

うん、私は同じ問題に出くわしましたが、幸いなことに最初の要素だけが必要です...-これは私がやったことです。

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

最後の要素も必要な場合-マップの順序を逆にする方法を調べます-一時変数に格納し、逆マップの最初の要素にアクセスします(したがって、最後の要素になります)一時変数。

ハッシュマップの順序を逆にする方法に関する適切な回答を次に示します。

Javaでハッシュマップを逆順に反復する方法

上記のリンクのヘルプを使用する場合は、投票してください:)これが誰かの助けになることを願っています。

0
ryvianstyron

LinkedHashMapは、最初、最後、または特定のオブジェクトを取得するメソッドを提供しませんが。

しかし、取得するのはかなり簡単です:

  • マップorderMap = new LinkedHashMap();
    Set al = orderMap.keySet();

現在、すべてのオブジェクトでイテレータを使用しています。任意のオブジェクトを取得できます。

0
rai.skumar