Java Stream 减少泛型类型的 ArrayList

Posted

技术标签:

【中文标题】Java Stream 减少泛型类型的 ArrayList【英文标题】:Java Stream reduce ArrayList of generic types 【发布时间】:2021-07-16 00:58:32 【问题描述】:

我有一个名为“Catalog”的文章(通用类型)的 ArrayList。

一篇文章有​​以下方法:

public int getUnitsInStore()
public long getUnitPrice()

为了获得目录中所有文章的总价值,我尝试使用 Java Stream Api 的 Reduce 方法。

我尝试了以下操作:

long value = 0;
    
value = catalog.stream().reduce((a,b) -> a.getUnitPrice()*b.getUnitsInStore());

但这给了我错误:

Type mismatch: cannot convert from Optional<Article> to long

我做错了什么?

【问题讨论】:

阅读 reduce 的文档:它会告诉您 a 和 b 是什么类型,以及它们究竟代表什么。 您的代码毫无意义。为什么要将一篇文章的价格乘以一篇完全不同文章的单位数?听起来你需要的是long total = catalog.stream().mapToLong(a -&gt; a.getUnitPrice() * a.getUnitsInStore()).sum() @Andreas:是的,现在我知道了。感谢您的评论。这很有帮助 【参考方案1】:

累加器 lambda 中的 ab 分别是流中的部分结果和下一个元素。由于您正在减少文章列表,因此部分结果是文章,您不能在文章中添加 Long。

所以在你的情况下,你可能想做这样的事情。

value = catalog.stream()
            .map(a -> a.getUnitPrice() * a.getUnitsInStore())
            .reduce(0L, Long::sum);

【讨论】:

非常感谢。知道我明白 .mapToLong(...).sum() 可能效率更高。【参考方案2】:

reduce 方法分为三种类型。 试试这段代码以更好地理解它

    /**
     * T reduce(T identity, BinaryOperator<T> accumulator)
     * 
     * identity = initial value
     * accumulator = first process initial value to first element of the stream,
     * then process the result with the next element in the stream
     */
     
    String name = Stream.of("T", "O", "M", "M", "Y")
            .reduce("", (a,n) -> a + n);
    System.out.println(name);
    
    int sum = Stream.of(1,2,3,4,5,6,7,8,9,10)
            .reduce(0, (a,n) -> a + n);
    System.out.println(sum);

    int multi = Stream.of(1,2,3,4,5,6,7,8,9,10)
            .reduce(1, (a,n) -> a * n);
    System.out.println(multi);
    
    
    /**
     * Optional<T> reduce(BinaryOperator<T> accumulator)
     * 
     */
    
    Optional<String> optName = Stream.of("T", "O", "M", "M", "Y")
            .reduce((a,n) -> a + n);
    
    if(optName.isPresent())
        System.out.println(" get from optional --> " + optName.get());
     
    
    /**
     * <U> U reduce​(U identity, BiFunction<U,​? super T,​U> accumulator, BinaryOperator<U> combiner)
     * 
     * This method signature is used when we are dealing with different types.
     * It allows Java to create intermediate reductions and then combine them at the end.
     */
    
    int total = Stream.of("R", "a", "z", "v", "a", "n")
        .reduce(0, (a,b) -> a + b.length(), (x,y) -> x + y);
    
            // 0 = initial value type int
            // a = int, must match the identity type
            // b.method() return type must match the a and the identity type
            // x,y from BinaryOperator 
    
    System.out.println(total);
    
    
    String names[] = "Bobby", "Mark", "Anthony", "Danna";
    
    int sumAll = Stream.of(names)
            .reduce(0, (a,b) -> a + b.length(), (x,y) -> x + y);
    System.out.println(sumAll);
    
    String csvNames = Stream.of(names)
            .reduce("", (a,b) -> a + b + ";");
    System.out.println(csvNames);
    

【讨论】:

非常感谢。这真的很有帮助 第三个可能有点棘手,但试试看 3 参数示例非常糟糕,因为使用int total = Stream.of("R", "a", "z", "v", "a", "n").mapToInt(String::length).sum() 会更好地实现。 3 参数版本不仅仅用于执行 map() 操作,它在结果确实是不同类型时使用,例如Collection 对象或用于收集多个结果(例如最小值、最大值和平均值)的 stats 对象。【参考方案3】:

我建议您使用 Stream.reduce() 重载,它需要两个参数:

identity:identity 元素既是归约的初始值,也是流中没有元素时的默认结果 accumulator:累加器函数,它接受两个参数:归约的部分结果和流的下一个元素。它返回一个新的部分结果。

因此,您将通过以下方式获得价值:

value = catalog
        .stream()
        .reduce(0L, (partialSum, nextElement) -> partialSum + nextElement.getUnitPrice() * nextElement.getUnitsInStore());

【讨论】:

Stream.reduce() 方法需要 1、2 或 3 个参数,具体取决于使用的重载。问题是尝试使用 1 参数版本。如果您愿意,您可以suggest 改用 2 参数版本,并解释您为什么建议这样做,但这不是这个答案的作用,所以它不是一个有用的答案。 没错。在我看来,最好的选择是带有 2 个参数的那个,所以 OP 可以定义初始值。我将更新答案以澄清 其中一个重载需要两个 args

以上是关于Java Stream 减少泛型类型的 ArrayList的主要内容,如果未能解决你的问题,请参考以下文章

在Java泛型类中,在构造函数级别添加额外的通用约束?

Java泛型变量限定

java 泛型数组

Java中泛型 类型擦除

java泛型

Java泛型