一次完成多次还原 - java

在流的单次通过中执行多个还原的惯用法是什么?即使需要一种以上的简化计算类型,即使违反了SRP,也仅仅是拥有一个大型的reducer类吗?

参考方案

大概您希望避免多次通过,因为管线阶段可能会很昂贵。或者,您希望避免收集中间值以便通过多个收集器运行它们,因为存储所有值的成本可能太高。

作为Brian Goetz noted,Collectors.summarizingInt将收集int值并对它们执行多次归约,从而返回称为IntSummaryStatistics的聚合结构。有类似的收集器,用于汇总doublelong值。

不幸的是,这些只能执行一组固定的归约,因此,如果您要进行与归约不同的归约,则必须编写自己的收集器。

这是一次使用多个不相关收集器的技术。我们可以使用peek()对流中的每个值进行破解,使其不受干扰。 peek()操作需要一个Consumer,因此我们需要一种使Collector适应Consumer的方法。 Consumer将成为收集器的累加器功能。但是,我们还需要调用收集器的供应商函数,并存储它创建的对象以传递给累加器函数。我们需要一种将结果从收集器中取出的方法。为此,我们将收集器包装在一个小助手类中:

public class PeekingCollector<T,A,R> {
    final Collector<T,A,R> collector;
    final A acc;

    public PeekingCollector(Collector<T,A,R> collector) {
        this.collector = collector;
        this.acc = collector.supplier().get();
    }

    public Consumer<T> peek() {
        if (collector.characteristics().contains(Collector.Characteristics.CONCURRENT))
            return t -> collector.accumulator().accept(acc, t);
        else
            return t -> {
                synchronized (this) {
                    collector.accumulator().accept(acc, t);
                }
            };
    }

    public synchronized R get() {
        return collector.finisher().apply(acc);
    }
}

要使用此功能,我们首先必须创建包装的收集器并挂在其上。然后,我们运行管道并调用peek,传递包装好的收集器。最后,我们在包装好的收集器上调用get以获得其结果。这是一个简单的示例,它过滤和排序一些单词,同时按首字母将它们分组:

    List<String> input = Arrays.asList(
        "aardvark", "crocodile", "antelope",
        "buffalo", "bustard", "cockatoo",
        "capybara", "bison", "alligator");

    PeekingCollector<String,?,Map<String,List<String>>> grouper =
        new PeekingCollector<>(groupingBy(s -> s.substring(0, 1)));

    List<String> output = input.stream()
                               .filter(s -> s.length() > 5)
                               .peek(grouper.peek())
                               .sorted()
                               .collect(toList());

    Map<String,List<String>> groups = grouper.get();
    System.out.println(output);
    System.out.println(groups);

输出为:

[aardvark, alligator, antelope, buffalo, bustard, capybara, cockatoo, crocodile]
{a=[aardvark, antelope, alligator], b=[buffalo, bustard], c=[crocodile, cockatoo, capybara]}

这有点麻烦,因为您必须为包装的收集器写出通用类型(这有点不寻常;通常都可以推断出它们)。但是,如果处理或存储流值的开销足够大,那么值得付出麻烦。

最后请注意,如果并行运行流,则可以从多个线程中调用peek()。因此,必须由synchronized块保护非线程安全的收集器。如果收集器是线程安全的,则无需在调用它时进行同步。为了确定这一点,我们检查收集器的CONCURRENT特性。如果运行并行流,则最好在groupingByConcurrent操作中放置一个并发收集器(例如toConcurrentMappeek),否则包装的收集器中的同步可能会导致瓶颈并减慢整个流的速度。

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…

JAVA:如何检查对象数组中的所有对象是否都是子类的对象? - java

我有一个对象数组。现在,我要检查所有这些对象是否都是MyObject的实例。有没有比这更好的选择:boolean check = true; for (Object o : justAList){ if (!(o instanceof MyObject)){ check = false; break; } } java大神给出的解决方案 如果您不喜欢循环,则…

Java-如何将此字符串转换为日期? - java

我从服务器收到此消息,我不明白T和Z的含义,2012-08-24T09:59:59Z将此字符串转换为Date对象的正确SimpleDateFormat模式是什么? java大神给出的解决方案 这是ISO 8601标准。您可以使用SimpleDateFormat simpleFormat = new SimpleDateFormat("yyyy-MM…