2016-11-02 16 views
7

Trong khi tôi đang thực hiện một bài tập lập trình nhỏ, tôi đã tình cờ gặp một số ClassCastException. Làm nền tôi đang đưa ra một phiên bản đơn giản của việc thực hiện để chứng minh vấn đề:ClassCastException khi sử dụng nhà cung cấp bản đồ tùy chỉnh trong nhóm theo

Cho một chuỗi mà chỉ các nhân vật A hoặc B chứa tính toán một bản đồ với các nhân vật như chìa khóa và số lần xuất hiện như các giá trị. Ngoài ra, bản đồ phải luôn chứa cả hai ký tự làm khóa (với giá trị bằng 0 nếu một ký tự bị thiếu trong chuỗi đầu vào).

Ví dụ:

  • "A" => {A=1, B=0}
  • "AAB" => {A=2, B=1}

giải pháp đầu tiên của tôi là như sau:

import static java.util.stream.Collectors.counting; 
import static java.util.stream.Collectors.groupingBy; 

public Map<Character, Long> createResult(String input) { 
    Map<Character, Long> map = input.chars() 
     .mapToObj(c -> (char) c) 
     .collect(groupingBy(c -> c, counting())); 

    map.putIfAbsent('A', 0L); 
    map.putIfAbsent('B', 0L); 
    return map; 
} 

Giải pháp này làm việc nhưng tôi muốn thử nếu nó là pos sible để cung cấp một bản đồ với giá trị mặc định cho groupingBy chức năng:

public HashMap<Character, Long> createResult2(String input) { 
    return input.chars() 
     .mapToObj(c -> (char) c) 
     .collect(groupingBy(c -> c, this::mapFactory, counting())); 
} 

private HashMap<Character, Long> mapFactory() { 
    HashMap<Character, Long> map = new HashMap<>(); 
    map.put('A', 0L); 
    map.put('B', 0L); 
    return map; 
} 

Khi gọi createResult2 với đầu vào A một ClassCastException được ném trong thời gian chạy:

 
java.lang.ClassCastException: java.lang.Long cannot be cast to [Ljava.lang.Object; 
    at java.util.stream.Collectors.lambda$groupingBy$45(Collectors.java:909) 
    at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169) 
    at java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250) 
    at java.lang.CharSequence$1CharIterator.forEachRemaining(CharSequence.java:149) 
    at java.util.Spliterators$IntIteratorSpliterator.forEachRemaining(Spliterators.java:1908) 
    at java.util.Spliterator$OfInt.forEachRemaining(Spliterator.java:693) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) 
    at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 
    at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499) 

bất cứ ai có thể giải thích tại sao điều này đang xảy ra?

+0

Luồng có vẻ quá mức cần thiết cho sự cố đơn giản này. Thậm chí tệ hơn, stacktrace không chứa gì về mã * của bạn gây ra lỗi. Đó sẽ là lý do đủ để tôi sử dụng một vòng lặp đơn giản. –

+0

Dòng cuối cùng trong stacktrace tương ứng với lệnh gọi 'collect' trong đoạn mã của tôi. – eee

+1

@LutzHorn Câu hỏi này là nhiều hơn về lý do tại sao điều này xảy ra và ít hơn về cách giải quyết bài tập. – eee

Trả lời

0

Tại sao không phải là một vòng lặp đơn giản?

private static Map<Character, Integer> count(String input) { 
    Map<Character, Integer> result = new HashMap<>(); 
    result.put('A', 0); 
    result.put('B', 0); 
    for (Character c : input.toCharArray()) { 
     result.put(c, result.get(c) + 1); 
    } 
    return result; 
} 
+0

Nó chậm hơn. Lấy hai truy cập vào bản đồ cho mỗi ký tự ('get' +' put') – talex

+4

@talex: chỉ cần thay thế 'result.put (c, result.get (c) + 1);' by 'result.merge (c, 1, Integer :: sum); 'và vấn đề được giải quyết. Nó sẽ hiệu quả hơn khi không khởi tạo trước bản đồ với số không, nhưng thực hiện một 'map.putIfAbsent ('A', 0L); map.putIfAbsent ('B', 0L); 'sau đó, giống như trong câu hỏi. – Holger

2

Có suy luận kiểu ma thuật có liên quan, nhưng nếu bạn muốn giải pháp ở đây là:

Thay

map.put('A', 0L); 
map.put('B', 0L); 

bởi

map.put('A', new Object[]{0L}); 
map.put('B', new Object[]{0L}); 

Nhưng tôi mạnh mẽ khuyến khích bạn của sử dụng giải pháp này trong thực tế. Chi tiết thực hiện có thể được thay đổi trong bất kỳ bản cập nhật nào và bản hack này hoạt động.

giải thích đây thêm về "tại sao" từ groupingBy javadoc

mapFactory - một chức năng mà khi gọi, tạo một bản đồ trống mới của các loại mong muốn

mapFactory là tham số thứ hai. Từ "trống" là rất quan trọng. Triển khai sử dụng bản đồ được tạo để lưu trữ các mảng long trong khi lặp lại và sau đó chuyển đổi chúng thành long. Nó hoạt động vì số lượng lớn đúc bên trong.

+2

Đừng làm điều đó với 'đối tượng mới [] {0L}'. Nó sẽ phá vỡ ngay khi bạn làm, hoặc sử dụng một JRE thay thế hoặc cập nhật lên Java 9. Trong Java 9, nó sẽ sử dụng 'long []' thay thế, tránh phần trên đầu trong khi đếm, nhưng các triển khai khác có thể sử dụng tạm thời khác các loại thùng chứa ... – Holger

+0

@Holger không thể đồng ý hơn. Đó là ý tưởng tồi để sử dụng giải pháp của tôi trong thực tế. – talex

+0

Nó hoàn toàn là trò đùa. Ngoài ra có rất nhiều đúc rõ ràng. Từ quan điểm thời gian chạy không có sự khác biệt, nhưng từ chất lượng của mã là thiết kế xấu. Tôi thấy liên kết của bạn và đồng ý rằng nó giải thích tình hình tốt hơn sau đó câu trả lời của tôi. – talex

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