2015-01-18 15 views
14

Tôi có cảm giác mình đang thiếu thứ gì đó ở đây. Tôi thấy mình làm như sauChuyển đổi luồng thành IntStream

private static int getHighestValue(Map<Character, Integer> countMap) { 
    return countMap.values().stream().mapToInt(Integer::intValue).max().getAsInt(); 
} 

Vấn đề của tôi là với việc chuyển đổi ngớ ngẩn từ Stream để IntStream qua mapToInt(Integer::intValue)

Có cách nào tốt hơn để làm việc chuyển đổi? tất cả điều này là để tránh sử dụng max() từ Stream, đòi hỏi đi qua một Comparator nhưng câu hỏi là cụ thể về chuyển đổi của Stream để IntStream

+1

bạn đã xem xét sử dụng 'max' (Comparator naturalOrder().)? –

+1

Và tại sao chính xác bạn nghĩ nó ngớ ngẩn? Bạn muốn trả về một int, vì vậy .mapToInt() có ý nghĩa ... – fge

+1

@fge vì tôi nghĩ rằng tôi đang lãng phí một cuộc gọi đến autobox các giá trị, mà sẽ cần O (n) hoạt động. Tôi hy vọng tôi có thể lấy luồng dưới dạng IntStream trực tiếp – Hilikus

Trả lời

6

Do xóa kiểu, triển khai Stream không có kiến ​​thức về loại phần tử của nó và không thể cung cấp cho bạn cả thao tác max được đơn giản hóa cũng như chuyển đổi sang phương thức IntStream.

Trong cả hai trường hợp, nó yêu cầu hàm, lần lượt là Comparator hoặc ToIntFunction để thực hiện thao tác sử dụng loại tham chiếu không xác định của các yếu tố của Stream.

Hình thức đơn giản cho các hoạt động bạn muốn thực hiện là

return countMap.values().stream().max(Comparator.naturalOrder()).get(); 

cho thực tế rằng trật tự tự nhiên so sánh được thực hiện như một singleton. Vì vậy, đó là công cụ so sánh duy nhất cung cấp cơ hội được công nhận bởi việc triển khai Streamnếu có bất kỳ tối ưu hóa nào về các yếu tố Comparable. Nếu không có tối ưu hóa như vậy, nó sẽ vẫn là biến thể có dấu chân bộ nhớ thấp nhất do tính chất đơn lẻ của nó.

Nếu bạn nhấn mạnh vào thực hiện một chuyển đổi của Stream một IntStream không có cách nào xung quanh việc cung cấp một ToIntFunction và không có singleton được xác định trước cho một loại Number::intValue chức năng, vì vậy sử dụng Integer::intValue đã là sự lựa chọn tốt nhất. Thay vào đó, bạn có thể viết i->i, ngắn hơn nhưng chỉ ẩn thao tác unboxing sau đó.

7

Tôi nhận ra bạn đang cố gắng để tránh một so sánh, nhưng bạn có thể sử dụng được xây dựng trong cho này bằng cách tham khảo Integer.compareTo:

private static int getHighestValue(Map<Character, Integer> countMap) { 
    return countMap.values().stream().max(Integer::compareTo).get(); 
} 

Hoặc như @fge cho thấy, sử dụng ::compare:

private static int getHighestValue(Map<Character, Integer> countMap) { 
    return countMap.values().stream().max(Integer::compare).get(); 
} 
+2

Thay vì kết thúc bằng 'get()' có thể, 'orElse (0)' hoặc 'orElseThrow (...)' có thể là "an toàn hơn"? – wassgren

+1

@wassgren Có, một trong những điều đó rất tuyệt, * đặc biệt là * nếu bạn có một bản đồ trống. Thành thật mà nói, tôi chỉ sử dụng 'get()' vì tôi đã tập trung nhiều hơn vào phần so sánh. Cuộc gọi tốt. – Todd

+1

Tôi upvoted này mặc dù nó không trả lời câu hỏi thực tế vì tôi sẽ sử dụng này để thay thế. Tuy nhiên, tôi chỉ muốn biết cách tốt nhất để chuyển đổi luồng thành IntStream là gì vì tôi thấy bản thân mình cần điều này trong các trường hợp khác. BTW, nó có thể là 'stream(). MapToInt (Integer :: intValue)' là cách tốt nhất – Hilikus

4

Một cách khác bạn có thể làm việc chuyển đổi là với lambda: mapToInt(i -> i). Cho dù bạn nên sử dụng một lambda hoặc một phương pháp tham khảo phương pháp được thảo luận chi tiết here, nhưng tóm tắt là bạn nên sử dụng bất cứ điều gì bạn tìm thấy dễ đọc hơn.

0

Nếu câu hỏi là "Tôi có thể tránh chuyển đổi chuyển đổi trong khi chuyển đổi từ Stream<T> sang IntStream?" một câu trả lời có thể là "Không có cách nào trong Java để làm cho loại chuyển đổi an toàn như vậy và làm cho nó một phần của giao diện Stream cùng một lúc".

Trên thực tế phương pháp mà chuyển đổi Stream<T>-IntStream mà không cần bộ chuyển đổi có thể được xem xét như thế này:

public interface Stream<T> { 
    // other methods 

    default IntStream mapToInt() { 
     Stream<Integer> intStream = (Stream<Integer>)this; 
     return intStream.mapToInt(Integer::intValue); 
    } 
} 

Vì vậy, nó giả được kêu gọi Stream<Integer> và sẽ thất bại trên các loại khác của dòng suối. Nhưng bởi vì luồng được đánh giá lười biếng và do loại xóa (nhớ rằng Stream<T> là chung chung) mã sẽ thất bại tại nơi dòng được tiêu thụ mà có thể là xa cuộc gọi mapToInt(). Và nó sẽ thất bại theo cách cực kỳ khó xác định nguồn gốc của vấn đề.

Giả sử bạn có mã:

public class IntStreamTest { 

    public static void main(String[] args) { 
     IntStream intStream = produceIntStream(); 
     consumeIntStream(intStream); 
    } 

    private static IntStream produceIntStream() { 
     Stream<String> stream = Arrays.asList("1", "2", "3").stream(); 
     return mapToInt(stream); 
    } 

    public static <T> IntStream mapToInt(Stream<T> stream) { 
     Stream<Integer> intStream = (Stream<Integer>)stream; 
     return intStream.mapToInt(Integer::intValue); 
    } 

    private static void consumeIntStream(IntStream intStream) { 
     intStream.filter(i -> i >= 2) 
       .forEach(System.out::println); 
    } 
} 

Nó sẽ thất bại trên consumeIntStream() cuộc gọi với:

Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
    at java.util.stream.ReferencePipeline$4$1.accept(ReferencePipeline.java:210) 
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) 
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481) 
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471) 
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151) 
    at java.util.stream.ForEachOps$ForEachOp$OfInt.evaluateSequential(ForEachOps.java:189) 
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) 
    at java.util.stream.IntPipeline.forEach(IntPipeline.java:404) 
    at streams.IntStreamTest.consumeIntStream(IntStreamTest.java:25) 
    at streams.IntStreamTest.main(IntStreamTest.java:10) 

Có stacktrace này làm bạn có thể nhanh chóng xác định rằng vấn đề là ở produceIntStream()mapToInt() được gọi trên luồng của loại sai?

Tất nhiên người ta có thể viết phương pháp chuyển đổi đó là loại an toàn vì nó chấp nhận bê tông Stream<Integer>:

public static IntStream mapToInt(Stream<Integer> stream) { 
    return stream.mapToInt(Integer::intValue); 
} 

// usage 
IntStream intStream = mapToInt(Arrays.asList(1, 2, 3).stream()) 

nhưng nó không phải là rất thuận tiện vì nó phá vỡ giao diện bản chất thông thạo của suối.

BTW:

hàm mở rộng Kotlin của phép để gọi một số mã như nó là một phần của giao diện lớp. Vì vậy, bạn có thể gọi phương pháp này loại an toàn như phương pháp một Stream<java.lang.Integer> 's:

// "adds" mapToInt() to Stream<java.lang.Integer> 
fun Stream<java.lang.Integer>.mapToInt(): IntStream { 
    return this.mapToInt { it.toInt() } 
} 

@Test 
fun test() { 
    Arrays.asList<java.lang.Integer>(java.lang.Integer(1), java.lang.Integer(2)) 
      .stream() 
      .mapToInt() 
      .forEach { println(it) } 
} 
Các vấn đề liên quan