2012-02-24 32 views
6

Vì vậy, tôi có một lỗi rất kỳ lạ. Tôi tình cờ gặp nó khi tôi ban đầu sử dụng một keySet() để lặp qua 10 khóa đầu tiên của TreeMap lớn. Một trong những chìa khóa đã trở về null, điều không nên có thể theo như sự hiểu biết của tôi. Vì vậy, tôi đã viết mã kiểm tra dưới đây:Key trong TreeMap trả về null

int i = 0; 
     for (Map.Entry<String, Integer> es : sortedMap.entrySet()){ 
      if (i >= 10) { 
       break; 
      } 

      if (sortedMap.containsKey(es.getKey())){ 
       System.out.println(es.getKey() + ":" + sortedMap.get(es.getKey())); 
      } else { 
       System.out.println("Key " + es.getKey() + " does not exist, yet..."); 
       System.out.println("This does work: " + es.getKey() + ":" + es.getValue()); 
       System.out.println("This does NOT work: " + es.getKey() + ":" + sortedMap.get(es.getKey())); 
      } 
      i++; 
     } 

Và có được kết quả như sau:

SOAP:967 
'excerpt'::679 
'type'::679 
Key 'author_url': does not exist, yet... 
This does work: 'author_url'::679 
This does NOT work: 'author_url'::null 
'date'::679 
Android:437 
TLS:295 
message:283 
server:230 
monthly:215 
<<<<<<<<<<<<<<<<<<<<DUMPING MAP! 
{SOAP=967, 'excerpt':=679, 'type':=679, 'author_url':=679, 'date':=679, Android=437, TLS=295, message=283, server=230, monthly=215... 

tôi cắt đứt các bản đồ sau khi top mười là có rất nhiều trong đó, nhưng tất cả của nó là chìa khóa có giá trị. Vì vậy, câu hỏi của tôi là: Tại sao tôi nhận được một null khi sử dụng chìa khóa để trực tiếp nhận được (key) từ TreeMap, nhưng EntrySet trả về khóa và giá trị chính xác?

Dưới đây là so sánh của tôi kể từ khi tôi đang đặt hàng trên Integer:

class ValueComparator implements Comparator<Object> { 

    Map<String, Integer> base; 
    public ValueComparator(Map<String, Integer> base) { 
     this.base = base; 
    } 

    public int compare(Object a, Object b) { 

    if ((Integer) base.get(a) < (Integer) base.get(b)) { 
     return 1; 
    } else if ((Integer) base.get(a) == (Integer) base.get(b)) { 
     return 0; 
    } else { 
     return -1; 
    } 
    } 
} 

Và TreeMap được xây dựng như sau:

ValueComparator bvc = new ValueComparator(allMatches); 
TreeMap<String, Integer> sortedMap = new TreeMap<String, Integer>(bvc); 
//Sort the HashMap 
sortedMap.putAll(allMatches); 

đâu allMatches là một HashMap<String, Integer>

+4

Bạn có đang sử dụng một trình so sánh khác thường cho TreeMap không? Nếu có, chúng ta có thể xem mã của nó không? Nó trông từ bãi chứa của bạn như thể bạn đang không sử dụng mặc định 'String' đặt hàng ... –

+0

@LouisWasserman Thêm so sánh của tôi. –

+2

Tại sao bạn có một hàm tạo và một thành viên trong lớp đó? Bộ so sánh thường được gọi cho mỗi phần tử, do đó bạn không nên tham khảo bản đồ kết quả. Toàn bộ mã của bạn sẽ hữu ích. – tom

Trả lời

1

Giải quyết vấn đề:

class ValueComparator implements Comparator<Object> { 

Map<String, Integer> base; 

public ValueComparator(Map<String, Integer> base) { 
    this.base = base; 
} 

public int compare(Object a, Object b) { 

    if (((Integer) base.get(a)).intValue() < ((Integer) base.get(b)).intValue()) { 
     return 1; 
    } else if (((Integer) base.get(a)).intValue() == ((Integer) base.get(b)).intValue()) { 
     return ((String)a).compareTo(((String)b)); 
    } else { 
     return -1; 
    } 
} 
} 

Điều này đi kèm với lợi ích bổ sung khi mang lại k eys có cùng giá trị theo thứ tự bảng chữ cái.

7

Từ thứ tự của lặp lại các chương trình TreeMap của bạn, chắc chắn trường hợp bạn đã sử dụng tùy chỉnh Comparator. [Nếu không lặp sẽ có được trong thứ tự từ điển]

Lưu ý rằng theo javadocs:

Các implementor phải đảm bảo rằng sgn (so sánh (x, y)) == -sgn (so sánh (y , x)) cho tất cả x và y. (Điều này ngụ ý rằng so sánh (x, y) phải ném một ngoại lệ nếu và chỉ khi so sánh (y, x) ném một ngoại lệ.)

Người triển khai cũng phải đảm bảo rằng mối quan hệ là transitive: ((so sánh) x, y)> 0) & & (so sánh (y, z)> 0)) ngụ ý so sánh (x, z)> 0.

Cuối cùng, người triển khai phải đảm bảo rằng so sánh (x, y) == 0 ngụ ý rằng sgn (so sánh (x, z)) == sgn (so sánh (y, z)) cho tất cả z.

Nếu số Comparator của bạn không áp dụng các quy tắc này - hành vi không được xác định, như có thể hiển thị kết quả lạ - như bạn thấy.

EDIT: [như phản ứng cho câu hỏi ngồi biên tập]
compartor của bạn sử dụng danh tính [operator==] để kiểm tra hai số nguyên.
Lưu ý rằng Integer là một đối tượng - và do đó operator== sẽ trả lại true chỉ khi đó là cùng một đối tượng.
Bạn nên sử dụng equals() để kiểm tra xem hai số nguyên là giống hệt nhau - hoặc thậm chí tốt hơn - sử dụng Integer.compareTo()

+0

Sử dụng compareTo làm cho TreeMap sụp đổ trên bất kỳ giá trị nào bằng nhau. '{monthly = 215, server = 230, message = 283, TLS = 295, Android = 475, trích đoạn = 679, SOAP = 967}' –

+0

Thật vậy, và đó là một gợi ý rằng mã của bạn thậm chí còn bị hỏng nhiều hơn. Bạn không thể sử dụng 'TreeMap' ở tất cả, không giống như bạn đang cố gắng sử dụng nó. –

+0

@DennisSullivan: Ngoài những gì Louis nói: Đọc các tài liệu java đính kèm. Bạn phải đảm bảo Trình so sánh của bạn thỏa mãn các điều khoản được viết - nếu không hành vi không được xác định. – amit

3

Vấn đề lớn nhất của bạn là việc bạn sử dụng == thay vì .equals trong so sánh giá trị của bạn là phá vỡ mọi thứ, bởi vì các phím khác nhau đang nhận được ánh xạ khác nhau Integer đối tượng với cùng một intValue(), đó là ném ra nhiều hơn những điều không thể đoán trước.

Nhưng nếu bạn đã khắc phục điều đó, thì TreeMap sẽ không cho phép bạn chèn nhiều khóa có cùng giá trị, điều này gần như chắc chắn cũng gây ra sự cố tinh tế.

Một giải pháp tốt hơn sẽ là một cái gì đó giống như this, nhưng về cơ bản, bạn nên điền vào một bản đồ mà không cần sắp xếp theo giá trị, sắp xếp các entrySet, và sau đó sao chép các mục (theo thứ tự) vào một bản đồ như LinkedHashMap mà không cần một so sánh, nhưng chỉ giữ các mục theo thứ tự chèn.

Bạn có thể có thể thay đổi bộ so sánh của bạn để nếu các giá trị giống nhau, nó cũng sẽ so sánh các phím. Điều này ít nhất sẽ cho phép bạn chèn nhiều khóa với cùng một giá trị ... nhưng nó vẫn là một giải pháp thực sự có nhiều rủi ro hơn nhiều so với giải pháp dựa trên LinkedHashMap như được mô tả ở trên.

+0

Hãy bỏ phiếu để giúp dòng suy nghĩ của tôi đến đúng câu trả lời. –

0

Bạn chỉ nên có:

class ValueComparator implements Comparator<Integer> { 


    public int compare(Integer a, Integer b) { 
     return a.compareTo(b); 
    } 
} 

Tiếp theo, bạn cần phải khởi tạo biểu đồ dạng cây của bạn với các so sánh và thêm tất cả các mục của bạn:

Treemap

+0

Anh ấy đang sử dụng nó để so sánh các giá trị, sẽ không hoạt động với 'TreeMap', không thực sự = ( –

+0

Bạn nói đúng.Tôi thouht ông đã được phân loại trên các phím (như thường lệ) nhưng ông đang phân loại trên các giá trị. Tôi đã đọc sai câu hỏi. Tất cả những gì tôi có thể nói là việc phân loại các giá trị thường không được thực hiện trên bản đồ. Danh sách phù hợp hơn cho điều đó. – tom