Java LinkedHashMap은 첫 번째 또는 마지막 항목을 얻습니다.


139

LinkedHashMap지도에 키를 입력 한 순서가 중요하기 때문에 사용 했습니다.

그러나 이제 첫 번째 (첫 번째 입력 한 항목) 또는 마지막에서 키의 가치를 얻고 싶습니다.

유사한 방법이 있어야 first()하고 last()그런이나 뭐?

첫 번째 키 항목을 얻으려면 반복자가 필요합니까? 그래서 내가 사용한 이유입니다 LinkedHashMap!

감사!


3
상황은 실제로 불행하다. 다음은 필요한 것을 제공하는 우선 순위가 낮은 기능 요청입니다. bugs.sun.com/view_bug.do?bug_id=6266354
Kevin Bourrillion 2009

stackoverflow.com/a/30984049/1808829 당신을 도울 수 있습니다
Ayaz Alifov

답변:


159

의 의미 LinkedHashMap는 여전히 의 의미가 아니라지도 의 의미입니다 LinkedList. 삽입 순서는 유지되지만 인터페이스의 측면이 아니라 구현 세부 사항입니다.

"첫 번째"항목을 얻는 가장 빠른 방법은 여전히 entrySet().iterator().next()입니다. "마지막"항목을 얻는 것은 가능하지만 마지막 항목에 .next()도달 할 때까지 전화를 걸어 전체 항목 세트를 반복 해야합니다. while (iterator.hasNext()) { lastElement = iterator.next() }

편집 : 그러나 JavaSE API를 넘어서고 싶다면 Apache Commons Collections 에는 자체 LinkedMap구현이 있으며,이 메소드에는 firstKeyand와 같은 메소드가 있으며 원하는 lastKey것을 수행합니다. 인터페이스가 훨씬 풍부합니다.


링크 된 맵에서 항목 (키, 값)을 첫 번째 항목으로 삽입해야합니다. 또는 인덱스 기반 삽입과 같은 것. 아파치 컬렉션에서도 가능합니까?
Kanagavelu Sugumar

2
"LinkedMap", "firstKey"및 "lastKey"링크는 오래되었습니다.
Josh

실제로는 Commons LinkedMap을 사용하여 lastKey를 얻은 다음 previousKey를 사용하여 역순으로 순회 할 수 있습니다.
rogerdpack

실제로는 CommonKeys LinkedMap을 사용하여 lastKey를 얻은 다음 previousKey를 사용하여 역순으로 순회 할 수 있습니다. 예를 들어 foreach 루프에서 사용하려는 경우 고유 한 Iterable을 작성할 수도 있습니다. 예 : stackoverflow.com/a/1098153/32453
rogerdpack

1
@ skaffman, 도와주세요 : 무엇입니까 mylinkedmap.entrySet().iterator().next() 있습니까 시간 복잡성 입니까? O (1)입니까?
tkrishtop 2019

25

마지막 항목을 얻기 위해 다음과 같은 작업을 시도 할 수 있습니까?

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

5
마지막 항목을 반복하고 유지하는 것이 여전히 빠릅니다. T last = null ; for( T item : linkedHashMap.values() ) last = item; 아니면 그런 것. 시간은 O (N)이지만 메모리는 O (1)입니다.
Florian F

@FlorianF 목록이 얼마나 큰지에 따라 다릅니다. 배열 솔루션은 빠를과 것이다 것없는 타협 메모리 컬렉션 그렇지 않으면 더 나은 솔루션으로 모두의 비트가 있다면 내가 특히 자바 8. 이후, 궁금 ... 이상 반복 그것을 통해 복용, 그 큰없는 경우
skinny_jones

1
@ skinny_jones : 왜 어레이 솔루션이 더 빠를까요? 여전히 전체 맵을 반복하는 것과 관련이 있습니다. 이제 반복이 명시 적 대신 JDK 메소드 안에 있다는 것입니다.
ruakh

17

나는 너무 늦었다는 것을 알고 있지만 특별한 것이 아니라 여기에 언급되지 않은 일부 대안을 제시하고 싶습니다. 누군가가 효율성을별로 신경 쓰지 않지만 더 단순하게 무언가를 원한다면 (아마도 한 줄의 코드로 마지막 입력 값을 찾으십시오), 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 구아바에서 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현재 구현 (자바 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);
}

1
하위 클래스 (또는 향후 구현)에 없는 경우에 대비 ClassCastException하여 추가 할 것이라고 생각 합니다. catchtailEntry
Paul Boddington

@PaulBoddington 나는 그것을 모두 캐치로 대체했습니다-일반적으로 권장되지 않지만 아마도 여기에 적합합니다.
assylias

7
아아

6

LinkedHashMap의 첫 번째 및 마지막 항목을 얻는 또 다른 방법은 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


4
toArray 메소드 자체는 다시 해시 맵을 반복합니다.) 따라서 배열을 만드는 데 낭비되는 몇 개의 추가주기와 배열에 할당 된 공간으로 인해 다소 비효율적입니다.
Durin

5

조금 더럽지 만 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하고 설정 하기가 쉬워야합니다 youngest...

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

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

그래도 항목 제거를 시작하면 모두 고장납니다. 그것을 막을 방법을 찾지 못했습니다.

합리적인 방법으로 머리 나 꼬리에 접근 할 수 없다는 것은 매우 성가신 일입니다 ...


시도는 좋지만 map.get이 호출 될 때 가장 오래된 항목이 업데이트됩니다. 따라서 put이 호출되지 않으면 eldestEntry는 이러한 경우에 업데이트되지 않습니다.
vishr

put을 호출하면 @vishr 가장 오래된 항목이 업데이트됩니다. (액세스 순서 LinkedHashMap를 가져 와서 입력)
Shiji.J

2

아마도 다음과 같은 것입니다 :

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

암시:

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

맵에 첫 번째로 삽입 된 키를 제공합니다. 첫 번째 키를 찾는 것은 O (1)에 있습니다. 여기서 문제는 마지막으로 삽입 된 키를 찾는 방법을 O (1)에서 찾는 것입니다.
Emad Aghayi

1

및 메소드 가있는 ConcurrentSkipListMap 을 사용 하는 것이 좋습니다.firstKey()lastKey()


1
ConcurrentSkipListMap에는 비교기 (또는 자연 비교기)가 필요하므로 입력 순서를 저장하기 위해 추가 작업을 수행해야합니다.
AlikElzin-kilaka

HashMap 및 특히 LinkedHashMap은 평균 O (1)의 액세스를 제공합니다. SkipList와 달리 O (logn)의 평균 액세스를 제공합니다.
AlikElzin-kilaka

"지도는 키의 자연 순서에 따라 정렬됩니다"

1

첫 번째 요소의 경우 entrySet().iterator().next()1 회 반복 후 반복 사용 을 중지하십시오. 마지막으로 가장 쉬운 방법은 map.put을 수행 할 때마다 키를 변수에 유지하는 것입니다.


0

linkedHashMap은 첫 번째, 마지막 또는 특정 객체를 얻는 방법을 제공하지 않습니다.

그러나 얻는 것이 아주 사소합니다.

  • 맵 orderMap = 새로운 LinkedHashMap ();
    설정 al = orderMap.keySet ();

이제 모든 객체에서 반복자를 사용합니다. 당신은 어떤 물건을 얻을 수 있습니다.


0

그래도 같은 문제가 발생했지만 운 좋게도 첫 번째 요소 만 필요합니다 ...-이것이 내가 한 일입니다.

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

마지막 요소도 필요한 경우-맵의 순서를 바꾸는 방법을 살펴보고 임시 변수에 저장하고 반전 된 맵의 첫 번째 요소에 액세스하십시오 (따라서 마지막 요소가됩니다). 임시 변수.

다음은 해시 맵을 역순으로 만드는 방법에 대한 좋은 답변입니다.

Java에서 해시 맵을 역순으로 반복하는 방법

위 링크의 도움말을 사용하는 경우 투표를 해주세요. :) 이것이 누군가를 도울 수 있기를 바랍니다.


0

오른쪽으로, 당신은 연결리스트의 끝까지 키셋을 수동으로 열거 한 다음, 키로 엔트리를 검색하고이 엔트리를 반환해야합니다.


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;
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.