Apakah mungkin untuk HashMap
mengembalikan nilai default untuk semua kunci yang tidak ditemukan di set?
Apakah mungkin untuk HashMap
mengembalikan nilai default untuk semua kunci yang tidak ditemukan di set?
Jawaban:
[Memperbarui]
Seperti dicatat oleh jawaban dan komentator lain, pada Java 8 Anda cukup menelepon Map#getOrDefault(...)
.
[Asli]
Tidak ada implementasi Peta yang melakukan hal ini dengan tepat tetapi akan sepele untuk mengimplementasikannya sendiri dengan memperluas HashMap:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
protected V defaultValue;
public DefaultHashMap(V defaultValue) {
this.defaultValue = defaultValue;
}
@Override
public V get(Object k) {
return containsKey(k) ? super.get(k) : defaultValue;
}
}
(v == null)
ke (v == null && !this.containsKey(k))
dalam kasus mereka sengaja menambahkan null
nilai. Saya tahu, ini hanya kasus sudut, tetapi penulis mungkin mengalami itu.
!this.containsValue(null)
. Ini agak berbeda dari !this.containsKey(k)
. The containsValue
solusi akan gagal jika beberapa lainnya kunci telah secara eksplisit diberi nilai null
. Misalnya: map = new HashMap(); map.put(k1, null); V v = map.get(k2);
Dalam hal ini, v
apakah masih akan null
, benar?
Di Java 8, gunakan Map.getOrDefault . Dibutuhkan kunci, dan nilai untuk kembali jika tidak ada kunci yang cocok ditemukan.
getOrDefault
sangat bagus, tetapi membutuhkan definisi default setiap kali peta diakses. Menentukan nilai default sekali juga akan memiliki manfaat keterbacaan saat membuat peta nilai statis.
private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Gunakan Commed ' DefaultedMap jika Anda merasa tidak ingin menciptakan kembali roda, misalnya,
Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"
Anda juga dapat meneruskan di peta yang ada jika Anda tidak bertanggung jawab untuk membuat peta di tempat pertama.
Java 8 memperkenalkan metode standar computeIfAbsent yang bagus untuk Map
antarmuka yang menyimpan nilai yang dikomputasi dengan malas sehingga tidak merusak kontrak peta:
Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));
Asal: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/
Disclamer: Jawaban ini tidak cocok dengan apa yang ditanyakan OP tetapi mungkin berguna dalam beberapa kasus mencocokkan judul pertanyaan ketika nomor kunci terbatas dan caching nilai yang berbeda akan menguntungkan. Seharusnya tidak digunakan dalam kasus yang berlawanan dengan banyak kunci dan nilai default yang sama karena ini akan membuang-buang memori.
Tidak bisakah Anda membuat metode statis yang melakukan ini?
private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
Anda cukup membuat kelas baru yang mewarisi HashMap dan menambahkan metode getDefault. Berikut ini contoh kode:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}
return defaultValue;
}
}
Saya pikir Anda tidak boleh mengesampingkan metode get (K key) dalam implementasi Anda, karena alasan yang ditentukan oleh Ed Staub dalam komentarnya dan karena Anda akan memutus kontrak antarmuka Peta (ini berpotensi menyebabkan beberapa sulit ditemukan) bug).
get
metode. Di sisi lain - solusi Anda tidak memungkinkan menggunakan kelas melalui antarmuka, yang mungkin sering terjadi.
Menggunakan:
myHashMap.getOrDefault(key, defaultValue);
Ini dilakukan secara default. Ia kembali null
.
HashMap
hanya untuk fungsi ini ketika null
baik-baik saja.
NullObject
pola, atau tidak ingin menyebarkan cek-nol di seluruh kode Anda - keinginan yang sepenuhnya saya mengerti.
Saya menemukan LazyMap cukup membantu.
Ketika metode get (Object) dipanggil dengan kunci yang tidak ada di peta, pabrik digunakan untuk membuat objek. Objek yang dibuat akan ditambahkan ke peta menggunakan kunci yang diminta.
Ini memungkinkan Anda melakukan sesuatu seperti ini:
Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();
Panggilan untuk get
menciptakan nilai default untuk kunci yang diberikan. Anda menentukan cara membuat nilai default dengan argumen pabrik untuk LazyMap.lazyMap(map, factory)
. Pada contoh di atas, peta diinisialisasi ke yang baru AtomicInteger
dengan nilai 0.
List<String>
- menggunakan jawaban yang diterima saya akan mengambil risiko menggunakan daftar yang sama untuk setiap kunci baru, daripada (katakanlah) new ArrayList<String>()
dari pabrik.
Tidak secara langsung, tetapi Anda dapat memperluas kelas untuk memodifikasi metode get-nya. Berikut ini adalah contoh siap pakai: http://www.java2s.com/Code/Java/Collections-Data-Structure/ExtendedVersionofjavautilHashMapthatprovidesanextendedgetmethodaccpetingadefaultvalue.htm
/**
* Extension of TreeMap to provide default value getter/creator.
*
* NOTE: This class performs no null key or value checking.
*
* @author N David Brown
*
* @param <K> Key type
* @param <V> Value type
*/
public abstract class Hash<K, V> extends TreeMap<K, V> {
private static final long serialVersionUID = 1905150272531272505L;
/**
* Same as {@link #get(Object)} but first stores result of
* {@link #create(Object)} under given key if key doesn't exist.
*
* @param k
* @return
*/
public V getOrCreate(final K k) {
V v = get(k);
if (v == null) {
v = create(k);
put(k, v);
}
return v;
}
/**
* Same as {@link #get(Object)} but returns specified default value
* if key doesn't exist. Note that default value isn't automatically
* stored under the given key.
*
* @param k
* @param _default
* @return
*/
public V getDefault(final K k, final V _default) {
V v = get(k);
return v == null ? _default : v;
}
/**
* Creates a default value for the specified key.
*
* @param k
* @return
*/
abstract protected V create(final K k);
}
Contoh penggunaan:
protected class HashList extends Hash<String, ArrayList<String>> {
private static final long serialVersionUID = 6658900478219817746L;
@Override
public ArrayList<Short> create(Short key) {
return new ArrayList<Short>();
}
}
final HashList haystack = new HashList();
final String needle = "hide and";
haystack.getOrCreate(needle).add("seek")
System.out.println(haystack.get(needle).get(0));
Saya perlu membaca hasil yang dikembalikan dari server di JSON di mana saya tidak bisa menjamin bidang akan ada. Saya menggunakan kelas org.json.simple.JSONObject yang berasal dari HashMap. Berikut adalah beberapa fungsi pembantu yang saya gunakan:
public static String getString( final JSONObject response,
final String key )
{ return getString( response, key, "" ); }
public static String getString( final JSONObject response,
final String key, final String defVal )
{ return response.containsKey( key ) ? (String)response.get( key ) : defVal; }
public static long getLong( final JSONObject response,
final String key )
{ return getLong( response, key, 0 ); }
public static long getLong( final JSONObject response,
final String key, final long defVal )
{ return response.containsKey( key ) ? (long)response.get( key ) : defVal; }
public static float getFloat( final JSONObject response,
final String key )
{ return getFloat( response, key, 0.0f ); }
public static float getFloat( final JSONObject response,
final String key, final float defVal )
{ return response.containsKey( key ) ? (float)response.get( key ) : defVal; }
public static List<JSONObject> getList( final JSONObject response,
final String key )
{ return getList( response, key, new ArrayList<JSONObject>() ); }
public static List<JSONObject> getList( final JSONObject response,
final String key, final List<JSONObject> defVal ) {
try { return response.containsKey( key ) ? (List<JSONObject>) response.get( key ) : defVal; }
catch( ClassCastException e ) { return defVal; }
}
Dalam proyek Jawa / Kotlin campuran juga mempertimbangkan Map.withDefault Kotlin .