2017-11-03 4 views
3

ich eine Liste eines benutzerdefinierten haben CallRecord ObjekteSumme und Max-Werte in einer einzigen Iteration

public class CallRecord { 

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

    private RecordType recordType; 
. 
. 
. 
} 

Es gibt zwei logische Bedingungen und der Ausgang jedes ist:

  1. Höchste SEQNUM, sum (Dauer)
  2. höchste SEQNUM, sum (Dauer), höchste causeForOutput

gemäß meinem Verständnis Stream.max(), Collectors.summarizingInt() usw. benötigen entweder mehrere Iterationen für das obige Ergebnis. Ich stieß auch auf einen thread, der kundenspezifischen Kollektor vorschlägt, aber ich bin unsicher.

Unten ist der einfache, Pre-Java 8-Code, der den Zweck dient:

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; 
     } 

} 
+0

Was ist Ihre Frage? –

+0

Konvertieren Sie den Pre-Java8-Code in den Java-Stream/Collector-Code IN EINZEL-Iteration –

+6

Die Antwort ist in der Frage. Sie benötigen einen benutzerdefinierten Collector. Oder, da Sie bereits Code haben, der dem Zweck dient, können Sie diesen Code einfach behalten. –

Antwort

5

Ihr Wunsch, alles zu tun, in einer einzige Iteration irrational ist. Sie sollten zunächst nach Einfachheit streben, bei Bedarf nach Performance, aber auch nicht auf einer einzelnen Iteration bestehen.

Die Leistung hängt von zu vielen Faktoren ab, um eine Vorhersage im Voraus zu treffen. Der Prozess des Iterierens (über eine einfache Sammlung) selbst ist nicht notwendigerweise eine teure Operation und kann sogar von einem einfacheren Schleifenkörper in einer Weise profitieren, die mehrere Durchläufe mit einer geradlinigen Operation effizienter macht als eine einzelne Durchquerung, die versucht, alles zu tun Einmal. Die einzige Möglichkeit, dies herauszufinden, besteht darin, die tatsächlichen Vorgänge zu messen.

den Betrieb Konvertieren von Operationen zum Streamen kann den Code vereinfachen, wenn Sie es gerade nach vorne, dh

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); 
} 

Wenn Sie immer noch mit der Tatsache, sich unwohl fühlen, dass es mehrere Iterationen, Sie könnten versuchen, schreiben ein benutzerdefinierter Collector, der alle Operationen gleichzeitig ausführt, aber das Ergebnis ist nicht besser als Ihre Schleife, weder in Bezug auf Lesbarkeit noch Effizienz.

Trotzdem würde ich Code-Duplizierung lieber vermeiden über versuchen, alles in einer Schleife, das heißt

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; 
    } 
} 
2

Mit Java-8 tun Sie es mit einem Collector ohne redudant Iteration gelöst werden.

Normalerweise können wir die Fabrikmethoden von Collectors verwenden, aber in Ihrem Fall müssen Sie eine benutzerdefinierte Collector implementieren, die eine Stream<CallRecord> zu einer Instanz von SummarizingCallRecord reduziert, die die Attribute cotains Sie benötigen.

Mutable Akkumulation/Ergebnistyp:

class SummarizingCallRecord { 
    private int highestSeqNum = 0; 
    private int sumDuration = 0; 

    // getters/setters ...  
} 

Individuelle Sammler:

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 
); 

Ausführungsbeispiel:

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 
0

Sie brauchen nicht und sollte die Logik durch Strom nicht implementieren API, weil die Tradition for-loop einfach genug ist und die Java 8 Stream API nicht kann Machen Sie es einfacher:

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. 
Verwandte Themen