Java LinkedHashMap को पहली या अंतिम प्रविष्टि मिलती है


139

मैंने उपयोग किया है LinkedHashMapक्योंकि यह महत्वपूर्ण है जिस क्रम में नक्शे में चाबियाँ दर्ज की गई हैं।

लेकिन अब मैं पहली (पहले दर्ज की गई प्रविष्टि) या आखिरी में कुंजी का मूल्य प्राप्त करना चाहता हूं।

क्या ऐसी कोई विधि होनी चाहिए first()और last()या जैसी कुछ होनी चाहिए ?

क्या मुझे केवल पहली कुंजी प्रविष्टि प्राप्त करने के लिए एक पुनरावृत्ति करने की आवश्यकता है? यही कारण है कि मैं इस्तेमाल किया LinkedHashMap!

धन्यवाद!


3
स्थिति वास्तव में दुर्भाग्यपूर्ण है। यहां निम्न (निम्न-प्राथमिकता) सुविधा अनुरोध है जो आपको प्रदान करेगा: Bugs.sun.com/view_bug.do?bug_id=6266354
केविन बॉरिलिन

stackoverflow.com/a/30984049/1808829 आपकी मदद कर सकता है
Ayaz Alifov

जवाबों:


159

का अर्थ विज्ञान LinkedHashMapअभी भी एक मानचित्र के उन लोगों के बजाय एक की है कि कर रहे हैं LinkedList। यह सम्मिलन आदेश को बरकरार रखता है, हाँ, लेकिन यह एक कार्यान्वयन विवरण है, बजाय इसके इंटरफ़ेस के एक पहलू के।

"पहली" प्रविष्टि प्राप्त करने का सबसे तेज़ तरीका अभी भी है entrySet().iterator().next()। "अंतिम" प्रविष्टि प्राप्त करना संभव है, लेकिन .next()आखिरी तक पहुंचने तक कॉल करके पूरे प्रवेश सेट पर पुनरावृत्ति करना होगा । while (iterator.hasNext()) { lastElement = iterator.next() }

संपादित करें : हालांकि, यदि आप जावाएसई एपीआई से परे जाने के इच्छुक हैं, तो अपाचे कॉमन्स कलेक्शंस का अपना LinkedMapकार्यान्वयन है, जिसमें इस तरह के तरीके हैं firstKeyऔर lastKey, जो आप की तलाश में हैं। इंटरफ़ेस काफी समृद्ध है।


मुझे अपने लिंक किए गए नक्शे में पहली प्रविष्टि के रूप में प्रविष्टि (कुंजी, मूल्य) डालना है; या इंडेक्स आधारित इंसर्शन जैसी कोई चीज। क्या यह अपाचे संग्रह के साथ संभव है?
कनागावेलु सुगुमार

2
"LinkedMap," "FirstKey," और "lastKey" लिंक बासी, फीए हैं।
जोश

लगता है कि आप वास्तव में शायद कॉमन्स LinkedMap का उपयोग करके इसे उल्टे क्रम में आगे बढ़ा सकते हैं, लास्ट में पाकर, इसके बाद पिछलेकेय का उपयोग करके पिछड़े ... अच्छा।
रोज़रपैक

लगता है कि आप वास्तव में शायद कॉमन्स लिंक्डपैप का उपयोग करके इसे उल्टे क्रम में आगे बढ़ा सकते हैं, लास्ट में पाकर, इसके बाद पिछलेकेय का उपयोग करके पिछड़े ... अच्छा। आप तब अपना खुद का Iterable भी लिख सकते थे यदि आप इसे foreach loops ex में इस्तेमाल करना चाहते थे: stackoverflow.com/a/1098153/32453
rogerdpack 16

1
@skaffman, क्या आप मदद कर सकते हैं: mylinkedmap.entrySet().iterator().next()समय जटिलता क्या है ? क्या यह ओ (1) है?
tkrishtop

25

क्या आप ऐसा कुछ करने की कोशिश कर सकते हैं (अंतिम प्रविष्टि प्राप्त करने के लिए):

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

5
यह अभी भी तेजी से चलना और अंतिम वस्तु रखना है। T last = null ; for( T item : linkedHashMap.values() ) last = item; या कुछ इस तरह का। यह समय में ओ (एन) है लेकिन स्मृति में ओ (1) है।
फ्लोरियन एफ

@FlorianF तो यह निर्भर करता है कि आपकी सूची कितनी बड़ी है। सरणी समाधान अधिक तेज़ होगा और मेमोरी को समझौता नहीं करेगा यदि संग्रह इतना बड़ा नहीं है, अन्यथा इसे बेहतर बनाने में अधिक समय लगना चाहिए ... मुझे आश्चर्य है कि अगर समाधान के रूप में दोनों का एक सा है, खासकर जावा 8. के ​​बाद से
skinny_jones

1
@skinny_jones: सरणी समाधान अधिक तेज़ क्यों होगा? इसमें अभी भी पूरे मानचित्र पर पुनरावृति शामिल है, यह सिर्फ इतना है कि अब पुनरावृत्ति स्पष्ट के बजाय JDK विधि के अंदर है।
21

17

मुझे पता है कि मैं बहुत देर से आया था, लेकिन मैं कुछ असाधारण, लेकिन कुछ मामलों का विकल्प देना चाहूंगा, जिनका यहां कोई उल्लेख नहीं है। उस स्थिति में जब कोई दक्षता के लिए इतना ध्यान नहीं रखता है, लेकिन वह अधिक सरलता के साथ कुछ चाहता है (शायद कोड की एक पंक्ति के साथ अंतिम प्रविष्टि मान पाएं), यह सब जावा 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। यह सूचियों और 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करने के लिए catchइस मामले में सिर्फ tailएक नहीं है Entryएक उपवर्ग (या एक भविष्य कार्यान्वयन) में।
पॉल बोडिंगटन

@PaBBdingdington मैंने इसे एक कैच के साथ बदल दिया है - सामान्य रूप से अनुशंसित नहीं है लेकिन शायद यहाँ उपयुक्त है।
assylias

7
आह "गंदे" उत्तर :)
rogerdpack

6

LinkedHashMap की पहली और आखिरी प्रविष्टि प्राप्त करने का एक और तरीका है सेट इंटरफ़ेस की "toArray" विधि का उपयोग करना।

लेकिन मुझे लगता है कि प्रवेश सेट में प्रविष्टियों पर पुनरावृत्ति और पहली और आखिरी प्रविष्टि प्राप्त करना एक बेहतर दृष्टिकोण है।

सरणी विधियों के उपयोग से फ़ॉर्म की चेतावनी "" ... के अनुरूप अनियंत्रित रूपांतरण की आवश्यकता होती है ... " जिसे ठीक नहीं किया जा सकता है [लेकिन केवल एनोटेशन @SuppressWarnings (" अनियंत्रित ") का उपयोग करके दबाया जा सकता है।

यहाँ "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
.Array विधि स्वयं फिर से हैशमैप पर पुनरावृति करेगी :) तो सरणी बनाने के लिए आवंटित स्थान और सरणी के लिए आवंटित किए गए कुछ अतिरिक्त चक्रों के कारण इसकी अधिक अक्षमता होगी।
डरिन

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

जब आप प्रविष्टियाँ निकालना शुरू करते हैं तो यह सब टूट जाता है; यह पता लगाने के लिए एक तरीका नहीं है कि कीचड़।

यह बहुत कष्टप्रद है कि आप अन्यथा समझदार तरीके से सिर या पूंछ तक पहुंच नहीं सकते हैं ...


अच्छा प्रयास करें, लेकिन map.get को लागू करने पर सबसे बड़ी प्रविष्टि अपडेट की जाती है। इसलिए eldestEntry को उन मामलों में अपडेट नहीं किया जाएगा, जब तक कि उसके बाद कॉल न किया जाए।
vishr

@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) में होगी। यहाँ समस्या ओ (1) में अंतिम सम्मिलित कुंजी खोजने के लिए एक रास्ता ढूंढ रही है।
इमाद अगाही

1

मैं समवर्तीSkipListMap का उपयोग करने की सलाह दूंगा जिसमें firstKey()और lastKey()तरीके हैं


1
ConcurrentSkipListMap को एक तुलनित्र (या प्राकृतिक तुलनित्र) की आवश्यकता होती है, इसलिए आपको उस क्रम को बचाने के लिए अतिरिक्त कार्य करने की आवश्यकता होती है जिसमें प्रविष्टियां डाली जाती हैं।
एलिकएल्ज़िन-किलाका

HashMap और विशेष रूप से LinkedHashMap O (1) के औसत में पहुंच प्रदान करता है - एक SkipList के विपरीत, जो O (logn) के औसत में पहुंच प्रदान करता है।
एलिकएल्ज़िन-किलाका

"नक्शा इसकी कुंजियों के प्राकृतिक क्रम के अनुसार क्रमबद्ध है"

1

पहले तत्व के लिए entrySet().iterator().next()और 1 पुनरावृत्ति के बाद पुनरावृति को रोकें। पिछले एक सबसे आसान तरीका के लिए एक चर में कुंजी को संरक्षित करना है जब भी आप एक map.put करते हैं।


0

हालांकि लिंक्डैश मैप पहले, अंतिम या किसी विशिष्ट वस्तु को प्राप्त करने के लिए कोई विधि प्रदान नहीं करता है।

लेकिन इसकी बहुत तुच्छ:

  • मैप ऑर्डरपार्ट = नया लिंक्डहाशपेज़ ();
    सेट अल = orderMap.keySet ();

अब अल वस्तु पर पुनरावृत्ति का उपयोग; आपको कोई भी वस्तु मिल सकती है।


0

हाँ, मैं एक ही समस्या में आया था, लेकिन सौभाग्य से मुझे केवल पहले तत्व की आवश्यकता है ... - यही मैंने इसके लिए किया था।

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

यदि आपको अंतिम तत्व की आवश्यकता है - तो मैं आपके मानचित्र के क्रम को उल्टा करने के बारे में देखूंगा - इसे एक अस्थायी चर में संग्रहीत करें, उल्टे नक्शे में पहले तत्व तक पहुंचें (इसलिए यह आपका अंतिम तत्व होगा), मार अस्थायी चर।

यहाँ हैशमैप ऑर्डर करने के तरीके के बारे में कुछ अच्छे जवाब दिए गए हैं:

जावा में रिवर्स ऑर्डर में हैशमैप कैसे करें

यदि आप उपरोक्त लिंक से मदद का उपयोग करते हैं, तो कृपया उन्हें वोट दें :) उम्मीद है कि यह किसी की मदद कर सकता है।


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;
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.