Java 8 在单个迭代中求和和和最大值

Java 8 在单个迭代中求和和和最大值,java-8,java-stream,collectors,Java 8,Java Stream,Collectors,我有一个自定义CallRecord对象的列表 public class CallRecord { private String callId; private String aNum; private String bNum; private int seqNum; private byte causeForOutput; private int duration; private RecordType recordType; . .

我有一个自定义CallRecord对象的列表

public class CallRecord {

    private String callId;
    private String aNum;
    private String bNum;
    private int seqNum;
    private byte causeForOutput;
    private int duration;

    private RecordType recordType;
.
.
.
}
有两个逻辑条件,每个逻辑条件的输出为:

  • 最高序号,总和(持续时间)
  • 最高序号、总和(持续时间)、最高原因输出
  • 根据我的理解,等等都需要对上述结果进行多次迭代。我还遇到了一位建议定制的收藏家,但我不确定

    下面是用于此目的的简单的Java 8之前的代码:

    if (...) {
    
        for (CallRecord currentRecord : completeCallRecords) {
            highestSeqNum = currentRecord.getSeqNum() > highestSeqNum ? currentRecord.getSeqNum() : highestSeqNum;
            sumOfDuration += currentRecord.getDuration();
        }
    
    } else {
        byte highestCauseForOutput = 0;
    
        for (CallRecord currentRecord : completeCallRecords) {
            highestSeqNum = currentRecord.getSeqNum() > highestSeqNum ? currentRecord.getSeqNum() : highestSeqNum;
            sumOfDuration += currentRecord.getDuration();
    
            highestCauseForOutput = currentRecord.getCauseForOutput() > highestCauseForOutput ? currentRecord.getCauseForOutput() : highestCauseForOutput;
            }
    
    }
    

    您希望在一次迭代中完成所有事情的愿望是不合理的。您应该首先追求简单性,必要时追求性能,但坚持一次迭代都不是

    性能取决于太多的因素,无法提前做出预测。迭代过程(在一个普通集合上)本身并不一定是一个昂贵的操作,甚至可以从一个更简单的循环体中获益,这使得使用一个直接操作的多个遍历比试图同时执行所有操作的单个遍历更有效。找到答案的唯一方法,是使用实际操作进行测量

    将操作转换为流操作可以简化代码,如果您直接使用它,即

    int highestSeqNum=
      completeCallRecords.stream().mapToInt(CallRecord::getSeqNum).max().orElse(-1);
    int sumOfDuration=
      completeCallRecords.stream().mapToInt(CallRecord::getDuration).sum();
    if(!condition) {
      byte highestCauseForOutput = (byte)
        completeCallRecords.stream().mapToInt(CallRecord::getCauseForOutput).max().orElse(0);
    }
    
    如果您仍然对存在多个迭代感到不舒服,您可以尝试编写一个自定义收集器,一次执行所有操作,但无论是在可读性还是效率方面,结果都不会比循环好

    尽管如此,我还是希望避免代码重复,而不是试图在一个循环中完成所有工作,即

    for(CallRecord currentRecord : completeCallRecords) {
        int nextSeqNum = currentRecord.getSeqNum();
        highestSeqNum = nextSeqNum > highestSeqNum ? nextSeqNum : highestSeqNum;
        sumOfDuration += currentRecord.getDuration();
    }
    if(!condition) {
        byte highestCauseForOutput = 0;
        for(CallRecord currentRecord : completeCallRecords) {
            byte next = currentRecord.getCauseForOutput();
            highestCauseForOutput = next > highestCauseForOutput? next: highestCauseForOutput;
        }
    }
    

    使用Java-8,您可以通过无需重复迭代的方式解决它

    通常,我们可以使用中的工厂方法,但在您的情况下,您需要实现一个自定义收集器,它将
    减少为
    摘要调用记录
    的实例,该实例包含您需要的属性

    可变累积/结果类型:

    class SummarizingCallRecord {
      private int highestSeqNum = 0;
      private int sumDuration = 0;
    
      // getters/setters ...      
    }
    
    自定义收集器:

    BiConsumer<SummarizingCallRecord, CallRecord> myAccumulator = (a, callRecord) -> {
      a.setHighestSeqNum(Math.max(a.getHighestSeqNum(), callRecord.getSeqNum()));
      a.setSumDuration(a.getSumDuration() + callRecord.getDuration());
    };
    
    BinaryOperator<SummarizingCallRecord> myCombiner = (a1, a2) -> {
      a1.setHighestSeqNum(Math.max(a1.getHighestSeqNum(), a2.getHighestSeqNum()));
      a1.setSumDuration(a1.getSumDuration() + a2.getSumDuration());
      return a1;
    };
    
    Collector<CallRecord, SummarizingCallRecord, SummarizingCallRecord> myCollector = 
      Collector.of(
        () -> new SummarizinCallRecord(), 
        myAccumulator, 
        myCombiner,
        // Collector.Characteristics.CONCURRENT/IDENTITY_FINISH/UNORDERED
      );
    
    BiConsumer myacculator=(a,callRecord)->{
    a、 setHighestSeqNum(Math.max(a.getHighestSeqNum(),callRecord.getSeqNum());
    a、 setSumDuration(a.getSumDuration()+callRecord.getDuration());
    };
    二进制运算符myCombiner=(a1,a2)->{
    a1.setHighestSeqNum(Math.max(a1.getHighestSeqNum(),a2.getHighestSeqNum());
    a1.setSumDuration(a1.getSumDuration()+a2.getSumDuration());
    返回a1;
    };
    收集器myCollector=
    收藏(
    ()->新建SummarincAllRecord(),
    我的累加器,
    麦康比纳,
    //Collector.Characteristics.CONCURRENT/IDENTITY\u FINISH/UNORDERED
    );
    
    执行示例:

    List<CallRecord> callRecords = new ArrayList<>();
    callRecords.add(new CallRecord(1, 100));
    callRecords.add(new CallRecord(5, 50));
    callRecords.add(new CallRecord(3, 1000));
    
    
    SummarizingCallRecord summarizingCallRecord  = callRecords.stream()
      .collect(myCollector);
    
    // Result:
    //    summarizingCallRecord.highestSeqNum = 5
    //    summarizingCallRecord.sumDuration = 1150
    
    List callRecords=new ArrayList();
    添加(新的CallRecord(1100));
    添加(新的通话记录(5,50));
    添加(新的CallRecord(31000));
    SummaringCallRecord SummaringCallRecord=callRecords.stream()
    .收集(myCollector);
    //结果:
    //SummaringCallRecord.highestSeqNum=5
    //SummaringCallRecord.sumDuration=1150
    
    您不需要也不应该通过流API实现逻辑,因为传统的
    for loop
    足够简单,Java 8流API无法使其更简单:

    int highestSeqNum = 0;
    long sumOfDuration = 0;
    byte highestCauseForOutput = 0; // just get it even if it may not be used. there is no performance hurt.
    for(CallRecord currentRecord : completeCallRecords) {
        highestSeqNum = Math.max(highestSeqNum, currentRecord.getSeqNum());
        sumOfDuration += currentRecord.getDuration();
        highestCauseForOutput = Math.max(highestCauseForOutput, currentRecord.getCauseForOutput());
    }
    
    // Do something with or without highestCauseForOutput.
    

    你的问题是什么?在一次迭代中将java8之前的代码转换为Java流/收集器代码答案在问题中。您需要一个自定义收集器。或者,由于您已经有了用于此目的的代码,您可以保留该代码。返回映射或自定义对象的自定义收集器将是最佳方法