2010-09-02 49 views
228

Kiểm tra sự tồn tại khóa trong HashMap có cần thiết không?Kiểm tra tồn tại khóa trong HashMap

Tôi có một HashMap với nói 1000 mục và tôi đang xem xét cải thiện hiệu quả. Nếu HashMap được truy cập rất thường xuyên, sau đó kiểm tra sự tồn tại khóa ở mọi truy cập sẽ dẫn đến một chi phí lớn. Thay vào đó nếu khóa không có mặt và do đó một ngoại lệ xảy ra, tôi có thể bắt được ngoại lệ. (khi tôi biết rằng điều này sẽ xảy ra hiếm khi). Điều này sẽ giảm bớt quyền truy cập vào HashMap một nửa.

Đây có thể không phải là một thực hành lập trình tốt, nhưng nó sẽ giúp tôi giảm số lượng truy cập. Hay tôi đang thiếu thứ gì đó ở đây?

[Cập nhật] Tôi không có giá trị null trong HashMap.

+8

"do đó và ngoại lệ xảy ra" - ngoại lệ là gì? Điều này sẽ không phải từ java.util.HashMap ... – serg10

Trả lời

379

Bạn có bao giờ lưu trữ giá trị null không? Nếu không, bạn chỉ có thể làm:

Foo value = map.get(key); 
if (value != null) { 
    ... 
} else { 
    // No such key 
} 

Nếu không, bạn thể chỉ cần kiểm tra cho sự tồn tại nếu bạn nhận được một giá trị null trả về:

Foo value = map.get(key); 
if (value != null) { 
    ... 
} else { 
    // Key might be present... 
    if (map.containsKey(key)) { 
     // Okay, there's a key but the value is null 
    } else { 
     // Definitely no such key 
    } 
} 
0

Tôi thường sử dụng thành ngữ

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

Điều này có nghĩa là bạn chỉ nhấn bản đồ hai lần nếu khóa bị thiếu

14

Bạn có nghĩa là bạn đã có mã như

if(map.containsKey(key)) doSomethingWith(map.get(key))

khắp nơi? Sau đó, bạn chỉ cần kiểm tra xem map.get(key) có trả về null hay không. Bằng cách này, HashMap không ném ngoại lệ cho các phím bị thiếu, nó trả về null thay vào đó. Trường hợp duy nhất cần containsKey khi bạn đang lưu trữ các giá trị null, để phân biệt giữa giá trị rỗng và giá trị bị thiếu, nhưng điều này thường được coi là thực hành không tốt.

55

Bạn sẽ không nhận được gì bằng cách kiểm tra xem khóa có tồn tại không. Đây là mã của HashMap:

@Override 
public boolean containsKey(Object key) { 
    Entry<K, V> m = getEntry(key); 
    return m != null; 
} 

@Override 
public V get(Object key) { 
    Entry<K, V> m = getEntry(key); 
    if (m != null) { 
     return m.value; 
    } 
    return null; 
} 

Chỉ cần kiểm tra nếu giá trị trả về cho get() là khác nhau từ null.

Đây là mã nguồn HashMap.


Resources:

+2

Điểm trong việc thể hiện một triển khai cụ thể của các phương pháp này là gì? – jarnbjo

+2

Để giải thích rằng trong hầu hết các trường hợp, hãy kiểm tra xem khóa có tồn tại sẽ mất khoảng thời gian đó không khi nhận giá trị.Vì vậy, nó sẽ không tối ưu hóa bất cứ điều gì để kiểm tra khóa thực sự tồn tại trước khi nhận được giá trị. Tôi biết đó là sự khái quát hóa nhưng nó có thể giúp hiểu được. –

+0

Liên kết tốt là http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/HashMap.java (OpenJDK có nguồn gốc rất lớn từ mã Sun) và có vẻ như tôi sai. Tôi đã so sánh phiên bản Java5 với Java6; chúng hoạt động khác nhau trong lĩnh vực này (nhưng cả hai đều đúng, cũng như các đoạn trích bạn đã đăng). –

0
  1. Nếu lớp chính là của bạn chắc chắn rằng hashCode() và equals () phương thức thực hiện.Về cơ bản việc truy cập vào HashMap phải là O (1) nhưng với việc thực hiện phương thức hashCode sai nó trở thành O (n), bởi vì giá trị với cùng một khóa băm sẽ được lưu trữ dưới dạng danh sách Liên kết.
32

Cách tốt hơn là sử dụng phương pháp containsKey của HashMap. Ngày mai soembody sẽ thêm null vào bản đồ, bạn nên phân biệt giữa khóa ở đó và khóa có giá trị null.

+0

Vâng. Hoặc phân lớp HashMap để ngăn chặn lưu trữ 'null' alltogether. – RobAu

+1

1+ đối với các kiểu nguyên thủy làm giá trị không cần thiết, không cần sử dụng câu trả lời này –

+0

Nó cũng thông thạo hơn để viết .containsKey() so với kiểm tra null. Chúng ta nên lo lắng nhiều hơn về việc dễ đọc, giúp tiết kiệm thời gian của nhà phát triển, hơn là về một số tối ưu hóa nhỏ, trong hầu hết các trường hợp. Ít nhất không tối ưu hóa trước khi nó trở nên cần thiết. – Max

4

Chỉ cần sử dụng containsKey() để làm rõ. Đó là nhanh chóng và giữ mã sạch sẽ và dễ đọc. Toàn bộ điểm của HashMap s là tra cứu chính nhanh, chỉ cần đảm bảo rằng hashCode()equals() được triển khai đúng.

3
if(map.get(key) != null || (map.get(key) == null && map.containsKey(key))) 
0

The Jon Skeet answer địa chỉ cũng hai kịch bản (bản đồ có giá trị null và không null giá trị) một cách hiệu quả.

Giới thiệu về các mục nhập số và mối quan tâm hiệu quả, tôi muốn thêm thứ gì đó.

Tôi có HashMap với mục nhập 1.000 mục và tôi đang xem xét cải thiện hiệu suất . Nếu HashMap được truy cập rất thường xuyên, thì kiểm tra sự tồn tại quan trọng ở mọi quyền truy cập sẽ dẫn đến chi phí lớn.

Bản đồ có 1.000 mục nhập không phải là một bản đồ lớn.
Cũng như bản đồ có 5.000 hoặc 10.000 mục nhập.
Map được thiết kế để thực hiện truy xuất nhanh với các thứ nguyên như vậy.

Bây giờ, giả định rằng hashCode() của các khóa bản đồ cung cấp phân phối tốt.

Nếu bạn có thể sử dụng Integer làm loại khóa, hãy thực hiện.
phương pháp hashCode() của nó là rất hiệu quả kể từ khi va chạm là không thể cho int giá trị duy nhất:

public final class Integer extends Number implements Comparable<Integer> { 
    ... 
    @Override 
    public int hashCode() { 
     return Integer.hashCode(value); 
    } 

    public static int hashCode(int value) { 
     return value; 
    } 
    ... 
} 

Nếu vì chìa khóa, bạn phải sử dụng một built-in loại như String ví dụ thường được sử dụng trong Map , bạn có thể có một số va chạm nhưng từ 1 nghìn đến vài nghìn đối tượng trong Map, bạn nên có rất ít trong số đó là phương pháp String.hashCode() cung cấp phân phối tốt.

Nếu bạn sử dụng loại tùy chỉnh, hãy ghi đè hashCode()equals() một cách chính xác và đảm bảo tổng thể rằng hashCode() cung cấp phân phối hợp lý.
Bạn có thể tham khảo mục 9 của Java Effective đề cập đến nó.
Dưới đây là một số post chi tiết cách đó.

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