2011-11-30 38 views
32

tôi bị bệnh của các mô hình sau:Java map.get (key) - tự động đặt (key) và trả về nếu khóa không tồn tại?

value = map.get(key); 
if (value == null) { 
    value = new Object(); 
    map.put(key, value); 
} 

Ví dụ này chỉ gãi bề mặt của mã thêm được viết khi bạn đã lồng bản đồ để đại diện cho một cấu trúc đa chiều.

Tôi chắc chắn có điều gì đó ở đâu đó tồn tại để tránh điều này, nhưng những nỗ lực của tôi trên Google không mang lại gì liên quan. Bất kỳ đề xuất?

+0

Trong sự tò mò, đối tượng bạn muốn đặt, nó chỉ là một đối tượng, hay loại khác nhau? Ngoài ra, nó đã được tạo ra hay nó chỉ được tạo ra nếu không có đối tượng nào tồn tại? –

+0

Loại được biết tại thời gian biên dịch. Thông thường, đó là một Chuỗi Bản đồ (đến Bản đồ) * thành Số nguyên. –

Trả lời

24

Các

java.util.concurrent.ConcurrentMap 

và từ Java 8

Java.util.Map 

putIfAbsent(K key, V value) 

mà trả về giá trị ánh xạ tới chìa khóa hoặc chèn giá trị nhất định và null nếu không có giá trị là ánh xạ cho khóa.

Nếu bạn cần đánh giá lười biếng của giá trị có

computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction) 
+3

Trả về: giá trị trước đó được liên kết với khóa được chỉ định hoặc null nếu không có ánh xạ cho khóa – user802421

+0

Hoàn hảo! Đó là những gì tôi đang tìm kiếm. Cảm ơn Roger. –

+5

Lưu ý rằng điều này buộc bạn phải tạo một đối tượng mới, ngay cả khi đã có sẵn một đối tượng trong bản đồ. Nếu bạn thực sự chỉ tạo một 'đối tượng mới()', điều này có thể không đáng kể. –

3

Vấn đề với mô hình này là bạn sẽ phải bằng cách nào đó xác định giá trị nên được sử dụng trong trường hợp get() lợi nhuận null.

Chắc chắn có các thư viện ở đó và IIRC cũng có một số bộ sưu tập mới hơn thực hiện điều đó, nhưng tiếc là tôi không nhớ đó là những bộ sưu tập nào.

Tuy nhiên, bạn có thể tự viết một phương thức tiện ích, miễn là bạn có cách tạo tiêu chuẩn giá trị mới. Một cái gì đó như thế này có thể hoạt động:

public static <K, V> V safeGet(K key, Map<K,V> map, Class<V> valueClass) throws /*add exceptions*/ { 
    V value = map.get(key); 
    if(value == null) { 
    value = valueClass.newInstance(); 
    map.put(key, value); 
    } 

    return value; 
} 

Lưu ý rằng bạn phải ném ngoại lệ phản chiếu hoặc xử lý chúng trong phương pháp. Ngoài ra, điều này yêu cầu valueClass để cung cấp một hàm tạo không có đối số. Ngoài ra, bạn có thể chỉ cần chuyển giá trị mặc định sẽ được sử dụng.

+0

Đó là đoạn mã hữu ích. Cảm ơn bạn. Tôi không biết làm thế nào để làm điều đó với Generics. –

1

CHỈNH SỬA: Lưu ý rằng tính năng được đề cập bên dưới bị từ chối lâu hơn và thay vào đó, sử dụng CacheBuilder.

Các Ổi thư viện có một "bản đồ máy tính", xem MapMaker.makeComputingMap(Function).

Map<String, Object> map = new MapMaker().makeComputingMap(
    new Function<String, Object>() { 
     public String apply(Stringt) { 
     return new Object(); 
     } 
    }); 

Nếu bạn cần các chức năng nhiều lần, giải nén nó vào một lớp tiện ích, và sau đó tạo bản đồ như thế này (nơi MyFunctions.NEW_OBJECT là Function Ví dụ static):

Map<String, Object> map = new MapMaker() 
    .makeComputingMap(MyFunctions.NEW_OBJECT); 
+0

'MapMaker' từ Google Ổi đã chết. Tham khảo: http://code.google.com/p/guava-libraries/wiki/MapMakerMigration – kevinarpe

+0

Tôi hiểu người Guava nghĩ rằng một thuật toán tự động 'Map.get()' là xấu, bởi vì nó phá vỡ hợp đồng 'Map'. Nếu bạn sử dụng bản đồ máy tính chỉ trong nội bộ, nó sẽ dễ dàng sử dụng một 'LoadingCache' thay thế. Tôi có thể thấy điểm của họ về một thảm họa đang chờ xảy ra khi chia sẻ bản đồ máy tính với mã không ngờ. Đối với trường hợp sử dụng đó, người ta nên sử dụng một hàm tiện ích 'safeGet()' giống như hàm được Thomas đưa ra trong câu trả lời của mình. –

0

Có lẽ tôi là không nhìn thấy toàn bộ vấn đề, nhưng làm thế nào về việc sử dụng thừa kế hoặc bố cục để thêm hành vi này vào đối tượng Bản đồ?

+0

Đó là điều tốt nhất tiếp theo tôi tìm thấy, vâng. Cảm ơn vi đa trả lơi. –

3

Bạn có thể sử dụng MutableMapgetIfAbsentPut() từ Eclipse Collections trả về giá trị được ánh xạ tới khóa hoặc chèn giá trị đã cho và trả về giá trị đã cho nếu không có giá trị nào được ánh xạ tới khóa.

Bạn có thể sử dụng một tài liệu tham khảo phương pháp để tạo ra một mới Object:

MutableMap<String, Object> map = Maps.mutable.empty(); 
Object value = map.getIfAbsentPut("key", Object::new); 

Hoặc bạn có thể trực tiếp tạo ra một mới Object:

MutableMap<String, Object> map = Maps.mutable.empty();  
Object value = map.getIfAbsentPut("key", new Object()); 

Trong ví dụ đầu tiên, đối tượng sẽ được tạo chỉ khi không có giá trị được ánh xạ tới khóa.

Trong ví dụ thứ hai, đối tượng sẽ được tạo bất kể.

Lưu ý: Tôi là cộng tác viên cho Bộ sưu tập Eclipse.

16

Java 8 cho biết thêm phương pháp tốt đẹp để các Map: compute, computeIfPresent, computeIfAbsent

Để đạt được những gì bạn cần:

Object existingOrCreated = map.computeIfAbsent(key, (k) -> new Object()); 
1

Nếu trong mọi trường hợp bạn cần phải nhận được một dữ liệu mặc định trong bản đồ của bạn nếu nó không hiện

map.getOrDefault(key, defaultValue); 

javadocs

+1

Thú vị, nhưng điều này không đặt nó trong bản đồ khi OP hỏi –

Các vấn đề liên quan