2012-04-02 48 views
5

Một số lớp JVM chính (chẳng hạn như String hoặc List implementations) thực hiện bằng bằng cách trả lại Σ 31^n * field_n.hashCode() cho mỗi field_n có liên quan cho phương pháp equals. Bên cạnh đó, cách tiếp cận này được đề xuất bởi Joshua Bloch trong Java hiệu quả (mục 9).Chiến lược triển khai hashCode

Tuy nhiên, các lớp khác như Map.Entry implementations tuân theo các quy tắc khác nhau. Ví dụ, các tài liệu Map.Entry khẳng định rằng mã hash của một Map.Entry nên

(e.getKey()==null ? 0 : e.getKey().hashCode())^
(e.getValue()==null ? 0 : e.getValue().hashCode()) 

này đôi khi có thể không thực tế để sử dụng trong các bảng băm, vì:

  • mã băm của tất cả các mục có cùng khóa và giá trị là 0,
  • hai mục e1 và e2 sao cho e1.key = e2.value và e1.value = e2.key có cùng mã băm.

Tại sao Java chọn đặc tả triển khai này cho Map.Entry hashCode thay vì, ví dụ: 31 * (e.getKey()==null ? 0 : e.getKey().hashCode()) + (e.getValue()==null ? 0 : e.getValue().hashCode())?

Sửa 1:

Để giúp tìm ra các vấn đề, đây là một ví dụ về mã hữu ích mà kết quả có hiệu suất rất kém do va chạm băm nếu nhiều mục có cùng một giá trị quan trọng và.

Phương pháp này tính toán tần suất của các mục nhập của các bản đồ khác nhau (sử dụng Multiset của Guava).

public static <K, V> Multiset<Map.Entry<K, V>> computeEntryCounts(
     Iterable<Map<K, V>> maps) { 
    ImmutableMultiset.Builder<Map.Entry<K, V>> result = ImmutableMultiset.builder(); 
    for (Map<K, V> map : maps) { 
     for (Map.Entry<K, V> entry : map.entrySet()) { 
      result.add(entry); 
     } 
    } 
    return result.build(); 
} 
+0

Việc triển khai của bạn không ảnh hưởng đến đầu ra cho khóa và giá trị là ** null **. Ngoài ra, không có nhiều hơn ** một mục nhập ** với một khóa trong Bản đồ bằng Java, một khóa chỉ xảy ra ** một lần ** trong bất kỳ việc triển khai Bản đồ nào. –

+0

Tôi biết, tôi đã giả định rằng các khóa của HashMap là các thể hiện của Map.Entry. Điều này có thể xảy ra nếu bạn muốn tính tổng số cho mỗi mục nhập khóa-giá trị trên một số Maps. – jpountz

+0

Tôi không thể thấy điều này sẽ ảnh hưởng đến trường hợp này, trừ khi bạn muốn đặt tất cả Map.Entry từ tất cả các bản đồ bên trong một bản đồ duy nhất để đếm chúng, nhưng điều này rõ ràng là sai. –

Trả lời

4

Tôi nghi ngờ có lý do chính đáng — Tôi nghĩ nó chỉ là sự giám sát — nhưng nó không phải là vấn đề nghiêm trọng. Nó sẽ chỉ xuất hiện nếu bạn có HashSet<Map.Entry<T,T>> hoặc HashMap<Map.Entry<T,T>,V>, thường không được thực hiện. (Edited thêm: Hoặc như Joachim Sauer chỉ ra dưới đây, một HashSet<Map<T,T>> hoặc một HashMap<Map<T,T>,V> — cũng không thường được thực hiện.)

Lưu ý rằng HashMap<K,V> không không sử dụng Map.Entry<K,V>.hashCode(), bởi vì nó nhìn lên mục bằng phím của họ chỉ, vì vậy nó chỉ sử dụng K.hashCode().

+1

Vì 'Map.hashCode()' được định nghĩa dưới dạng 'hashCode' của các phần tử' Map.Entry', nó cũng xuất hiện trong một 'Bản đồ , V2>'. Hoặc 'Đặt >'. Nhưng những người có thể dễ dàng tránh được cũng (và thường nên tránh vì lý do dễ đọc và thiết kế là tốt). –

+0

@ JoachimSauer: Tốt, cảm ơn bạn! – ruakh

+0

Tất cả những gì cần thiết để chạy vào vấn đề này là 'someHashSet.addAll (someMap.entrySet())' hoặc một cái gì đó như thế này [Vấn đề ổi] (http://code.google.com/p/guava-libraries/issues/detail ? id = 458). Tất cả các mục như "a" => "a" được đảm bảo va chạm và hiệu suất được đảm bảo là khủng khiếp (hoặc ít nhất là ít hơn tối ưu kể từ JDK 8). Bạn hiếm khi chạy vào nó, nhưng khi bạn làm nó khá xấu. – maaartinus

0

Đoán cá nhân của tôi là, hashCode cũng phải nhanh.

Vì bạn được phép ghi đè hashCode, không có vấn đề gì với nó. Thay đổi nó khi bạn biết thuật toán phù hợp hơn cho trường hợp của bạn

+2

Xin lỗi, nhưng điều này không đúng. 'Map.Entry' là một giao diện *. Nó không mô tả một thuật toán * mặc định * mà * nó * thực hiện nhưng các lớp con có thể ghi đè lên; thay vào đó, nó quy định một thuật toán cụ thể mà người triển khai được yêu cầu thực hiện. – ruakh

+0

Khi nhu cầu mặt trời/oracle trong mọi trường hợp thực hiện nhất định, họ phải sử dụng một lớp trừu tượng thay vì một giao diện. –

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