Có thể có HashMap
giá trị trả về mặc định cho tất cả các khóa không tìm thấy trong tập hợp không?
Có thể có HashMap
giá trị trả về mặc định cho tất cả các khóa không tìm thấy trong tập hợp không?
Câu trả lời:
[Cập nhật]
Như đã lưu ý bởi các câu trả lời và nhận xét khác, kể từ Java 8, bạn có thể chỉ cần gọi Map#getOrDefault(...)
.
[Nguyên]
Không có triển khai Bản đồ nào thực hiện điều này một cách chính xác nhưng sẽ rất đơn giản khi thực hiện bằng cách mở rộng 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)
để (v == null && !this.containsKey(k))
trong trường hợp họ cố tình thêm một null
giá trị. Tôi biết, đây chỉ là một trường hợp góc, nhưng tác giả có thể gặp phải nó.
!this.containsValue(null)
. Điều này là khác biệt tinh tế từ !this.containsKey(k)
. Các containsValue
giải pháp sẽ thất bại nếu một số khác quan trọng đã được chỉ định một cách rõ ràng giá trị của null
. Ví dụ: map = new HashMap(); map.put(k1, null); V v = map.get(k2);
Trong trường hợp này, v
vẫn sẽ null
, đúng không?
Trong Java 8, sử dụng Map.getOrDefault . Nó nhận khóa và giá trị trả về nếu không tìm thấy khóa khớp nào.
getOrDefault
là rất đẹp, nhưng yêu cầu định nghĩa mặc định mỗi khi bản đồ được truy cập. Xác định giá trị mặc định một lần cũng sẽ có lợi ích dễ đọc khi tạo bản đồ tĩnh của các giá trị.
private static String get(Map map, String s) { return map.getOrDefault(s, "Error"); }
Sử dụng Commons' DefaultedMap nếu bạn không cảm thấy như reinventing the wheel, ví dụ:
Map<String, String> map = new DefaultedMap<>("[NO ENTRY FOUND]");
String surname = map.get("Surname");
// surname == "[NO ENTRY FOUND]"
Bạn cũng có thể vượt qua trong một bản đồ hiện có nếu bạn không phụ trách việc tạo bản đồ ở nơi đầu tiên.
Java 8 đã giới thiệu một phương thức mặc định compute IfAbsent đẹp mắt để Map
giao diện lưu trữ giá trị được tính toán lười biếng và do đó không phá vỡ hợp đồng bản đồ:
Map<Key, Graph> map = new HashMap<>();
map.computeIfAbsent(aKey, key -> createExpensiveGraph(key));
Xuất xứ: http://blog.javabien.net/2014/02/20/loadingcache-in-java-8-without-guava/
Tuyên bố từ chối trách nhiệm: Câu trả lời này không khớp chính xác với những gì OP yêu cầu nhưng có thể hữu ích trong một số trường hợp khớp với tiêu đề của câu hỏi khi số khóa bị giới hạn và bộ nhớ đệm của các giá trị khác nhau sẽ có lợi. Nó không nên được sử dụng trong trường hợp ngược lại với nhiều khóa và cùng giá trị mặc định vì điều này sẽ không lãng phí bộ nhớ.
Bạn có thể tạo một phương thức tĩnh thực hiện chính xác điều này không?
private static <K, V> V getOrDefault(Map<K,V> map, K key, V defaultValue) {
return map.containsKey(key) ? map.get(key) : defaultValue;
}
Bạn có thể chỉ cần tạo một lớp mới kế thừa HashMap và thêm phương thức getDefault. Đây là một mã mẫu:
public class DefaultHashMap<K,V> extends HashMap<K,V> {
public V getDefault(K key, V defaultValue) {
if (containsKey(key)) {
return get(key);
}
return defaultValue;
}
}
Tôi nghĩ rằng bạn không nên ghi đè phương thức get (khóa K) trong quá trình triển khai của mình, vì những lý do được Ed Staub chỉ định trong nhận xét của anh ấy và vì bạn sẽ phá vỡ hợp đồng của giao diện Bản đồ (điều này có thể dẫn đến một số khó tìm lỗi).
get
phương thức. Mặt khác - giải pháp của bạn không cho phép sử dụng lớp thông qua giao diện, thường có thể là trường hợp.
Sử dụng:
myHashMap.getOrDefault(key, defaultValue);
Nó làm điều này theo mặc định. Nó trở lại null
.
HashMap
chỉ cho chức năng này khi null
hoàn toàn ổn.
NullObject
mẫu hoặc không muốn phân tán kiểm tra null trong toàn bộ mã của bạn - một mong muốn tôi hoàn toàn hiểu được.
Tôi thấy LazyMap khá hữu ích.
Khi phương thức get (Object) được gọi với một khóa không tồn tại trong bản đồ, nhà máy được sử dụng để tạo đối tượng. Đối tượng đã tạo sẽ được thêm vào bản đồ bằng phím được yêu cầu.
Điều này cho phép bạn làm một cái gì đó như thế này:
Map<String, AtomicInteger> map = LazyMap.lazyMap(new HashMap<>(), ()->new AtomicInteger(0));
map.get(notExistingKey).incrementAndGet();
Cuộc gọi để get
tạo một giá trị mặc định cho khóa đã cho. Bạn chỉ định cách tạo giá trị mặc định với đối số gốc LazyMap.lazyMap(map, factory)
. Trong ví dụ trên, bản đồ được khởi tạo thành một bản mớiAtomicInteger
với giá trị 0.
List<String>
- sử dụng câu trả lời được chấp nhận Tôi sẽ mạo hiểm khi sử dụng cùng một danh sách cho mỗi khóa mới, thay vì (nói) a new ArrayList<String>()
từ một nhà máy.
Không trực tiếp, nhưng bạn có thể mở rộng lớp để sửa đổi phương thức get của nó. Dưới đây là một ví dụ sẵn sàng để sử dụng: http://www.java2s.com/Code/Java/Collections-Data-Str struct / ExtendsVersionofjavautilHashMapthatprovidesanextendsgetmethodaccpetingadefaultvalue.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);
}
Cách sử dụng ví dụ:
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));
Tôi cần đọc kết quả trả về từ máy chủ trong JSON nơi tôi không thể đảm bảo các trường sẽ có mặt. Tôi đang sử dụng lớp org.json.simple.JSONObject có nguồn gốc từ HashMap. Dưới đây là một số chức năng trợ giúp tôi sử dụng:
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; }
}
Trong các dự án Java / Kotlin hỗn hợp cũng xem xét Map.withDefault của Kotlin .