Saya telah menggunakan LinkedHashMap
karena itu penting urutan kunci yang dimasukkan di peta.
Tapi sekarang saya ingin mendapatkan nilai kunci di tempat pertama (entri yang pertama masuk) atau yang terakhir.
Haruskah ada metode seperti first()
dan last()
atau sesuatu seperti itu?
Apakah saya perlu memiliki iterator untuk mendapatkan entri kunci pertama? Itulah mengapa saya menggunakan LinkedHashMap
!
Terima kasih!
java
dictionary
linkedhashmap
maiky
sumber
sumber
Jawaban:
Semantik
LinkedHashMap
masih berupa Peta, bukan dari aLinkedList
. Itu mempertahankan urutan penyisipan, ya, tapi itu detail implementasi, bukan aspek antarmukanya.Cara tercepat untuk mendapatkan entri "pertama" adalah diam
entrySet().iterator().next()
. Mendapatkan entri "terakhir" dimungkinkan, tetapi akan memerlukan pengulangan atas seluruh entri yang ditetapkan dengan menelepon.next()
hingga Anda mencapai yang terakhir.while (iterator.hasNext()) { lastElement = iterator.next() }
sunting : Namun, jika Anda ingin melampaui JavaSE API, Apache Commons Collections memiliki
LinkedMap
implementasinya sendiri , yang memiliki metode sepertifirstKey
danlastKey
, yang melakukan apa yang Anda cari. Antarmukanya jauh lebih kaya.sumber
mylinkedmap.entrySet().iterator().next()
itu kompleksitas waktu? Apakah itu O (1)?Bisakah Anda mencoba melakukan sesuatu seperti (untuk mendapatkan entri terakhir):
linkedHashMap.entrySet().toArray()[linkedHashMap.size() -1];
sumber
T last = null ; for( T item : linkedHashMap.values() ) last = item;
Atau semacam itu. Ini adalah O (N) dalam waktu tetapi O (1) dalam memori.Saya tahu bahwa saya datang terlambat tetapi saya ingin menawarkan beberapa alternatif, bukan sesuatu yang luar biasa tetapi beberapa kasus yang tidak disebutkan di sini. Jika seseorang tidak terlalu peduli dengan efisiensi tetapi dia menginginkan sesuatu yang lebih sederhana (mungkin menemukan nilai entri terakhir dengan satu baris kode), semua ini akan menjadi cukup disederhanakan dengan hadirnya Java 8 . Saya memberikan beberapa skenario yang berguna.
Demi kelengkapan, saya membandingkan alternatif ini dengan solusi array yang sudah disebutkan dalam posting ini oleh pengguna lain. Saya merangkum semua kasus dan saya pikir itu akan berguna (ketika kinerja itu penting atau tidak) terutama untuk pengembang baru, selalu tergantung pada masalah setiap masalah
Alternatif yang Mungkin
Penggunaan Metode Array
Saya mengambilnya dari jawaban sebelumnya untuk membuat perbandingan berikut. Solusi ini milik @feresr.
public static String FindLasstEntryWithArrayMethod() { return String.valueOf(linkedmap.entrySet().toArray()[linkedmap.size() - 1]); }
Penggunaan Metode ArrayList
Mirip dengan solusi pertama dengan kinerja yang sedikit berbeda
public static String FindLasstEntryWithArrayListMethod() { List<Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer, String>>(linkedmap.entrySet()); return entryList.get(entryList.size() - 1).getValue(); }
Kurangi Metode
Metode ini akan mengurangi himpunan elemen hingga mendapatkan elemen aliran terakhir. Selain itu, ini hanya akan mengembalikan hasil deterministik
public static String FindLasstEntryWithReduceMethod() { return linkedmap.entrySet().stream().reduce((first, second) -> second).orElse(null).getValue(); }
Metode SkipFunction
Metode ini akan mendapatkan elemen terakhir dari aliran hanya dengan melewatkan semua elemen sebelumnya
public static String FindLasstEntryWithSkipFunctionMethod() { final long count = linkedmap.entrySet().stream().count(); return linkedmap.entrySet().stream().skip(count - 1).findFirst().get().getValue(); }
Alternatif Iterable
public static String FindLasstEntryWithGuavaIterable() { return Iterables.getLast(linkedmap.entrySet()).getValue(); }
Ini kode sumber lengkapnya
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]); } }
Berikut adalah output dengan performa masing-masing metode
FindLasstEntryWithArrayListMethod : took 0.162 milliseconds FindLasstEntryWithArrayMethod : took 0.025 milliseconds FindLasstEntryWithReduceMethod : took 2.776 milliseconds FindLasstEntryWithSkipFunctionMethod : took 3.396 milliseconds FindLasstEntryWithGuavaIterable : took 11 milliseconds
sumber
LinkedHashMap
implementasi saat ini (Java 8) melacak ekornya. Jika kinerja menjadi perhatian dan / atau peta berukuran besar, Anda dapat mengakses bidang itu melalui refleksi.Karena penerapannya dapat berubah, mungkin ada baiknya untuk memiliki strategi fallback juga. Anda mungkin ingin mencatat sesuatu jika pengecualian dilempar sehingga Anda tahu bahwa implementasinya telah berubah.
Ini bisa terlihat seperti:
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); }
sumber
ClassCastException
untukcatch
berjaga-jagatail
bukanEntry
dalam subclass (atau implementasi di masa depan).Satu cara lagi untuk mendapatkan entri pertama dan terakhir dari LinkedHashMap adalah dengan menggunakan
toArray()
metode Set interface.Tapi saya pikir mengulangi entri dalam set entri dan mendapatkan entri pertama dan terakhir adalah pendekatan yang lebih baik.
Penggunaan metode array mengarah ke peringatan bentuk "... membutuhkan konversi yang tidak dicentang agar sesuai dengan ..." yang tidak dapat diperbaiki [tetapi hanya dapat disembunyikan dengan menggunakan anotasi
@SuppressWarnings("unchecked")
].Berikut adalah contoh kecil untuk mendemonstrasikan penggunaan
toArray()
metode: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
sumber
Ini agak kotor, tetapi Anda dapat mengganti
removeEldestEntry
metode LinkedHashMap, yang mungkin cocok untuk Anda lakukan sebagai anggota anonim pribadi: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; } };
Jadi Anda akan selalu bisa mendapatkan entri pertama pada
eldest
anggota Anda . Ini akan diperbarui setiap kali Anda melakukanput
.Ini juga harus mudah untuk mengganti
put
dan mengaturyoungest
...@Override public Splat put(Integer key, Splat value) { youngest = value; return super.put(key, value); }
Semuanya rusak ketika Anda mulai menghapus entri; belum menemukan cara untuk menutupinya.
Sangat menjengkelkan bahwa Anda tidak bisa mendapatkan akses ke kepala atau ekor dengan cara yang masuk akal ...
sumber
Mungkin sesuatu seperti ini:
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; }
sumber
Saran:
sumber
Saya akan merekomendasikan menggunakan ConcurrentSkipListMap yang memiliki
firstKey()
danlastKey()
metodesumber
Untuk penggunaan elemen pertama
entrySet().iterator().next()
dan hentikan iterasi setelah 1 iterasi. Untuk yang terakhir, cara termudah adalah dengan mempertahankan kunci dalam variabel setiap kali Anda melakukan map.put.sumber
Ya, saya menemukan masalah yang sama, tapi untungnya saya hanya membutuhkan elemen pertama ... - Inilah yang saya lakukan untuk itu.
private String getDefaultPlayerType() { String defaultPlayerType = ""; for(LinkedHashMap.Entry<String,Integer> entry : getLeagueByName(currentLeague).getStatisticsOrder().entrySet()) { defaultPlayerType = entry.getKey(); break; } return defaultPlayerType; }
Jika Anda membutuhkan elemen terakhir juga - saya akan melihat cara membalik urutan peta Anda - menyimpannya dalam variabel temp, akses elemen pertama di peta terbalik (oleh karena itu itu akan menjadi elemen terakhir Anda), bunuh variabel temp.
Berikut beberapa jawaban bagus tentang cara membalik urutan peta hash:
Cara mengiterasi hashmap dalam urutan terbalik di Java
Jika Anda menggunakan bantuan dari tautan di atas, tolong beri mereka suara :) Semoga ini dapat membantu seseorang.
sumber
benar, Anda harus secara manual menghitung kumpulan kunci hingga akhir daftar tertaut, kemudian mengambil entri dengan kunci dan mengembalikan entri ini.
sumber
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; }
sumber
Meskipun linkedHashMap tidak menyediakan metode apa pun untuk mendapatkan objek pertama, terakhir, atau tertentu.
Tapi cukup sepele untuk mendapatkannya:
Map<Integer,String> orderMap = new LinkedHashMap<Integer,String>(); Set<Integer> al = orderMap.keySet();
sekarang menggunakan iterator pada
al
objek; Anda bisa mendapatkan benda apapun.sumber