Ich habe Angst, dass mit Streams und Lambdas, Ihre Leistung leiden kann. Ihre aktuelle Lösung gibt den ersten gültigen und analysierbaren Knoten zurück. Es ist jedoch nicht möglich, eine Operation im Stream wie for-each (source) zu unterbrechen.
Da Sie zwei verschiedene Ausgaben haben können (zurückgegebenes Ergebnis oder geworfene Ausnahme), ist dies nicht mit einem einzelnen Zeilenausdruck möglich.
Hier ist, was ich gefunden habe. Es kann Ihnen ein paar Ideen:
public static Result match(Response rsp) throws Exception {
Map<Boolean, List<Object>> collect = rsp.getFirstNodes().stream()
.flatMap(firstNode -> firstNode.getSndNodes().stream()) // create stream of SndNodes
.filter(SndNode::isValid) // filter so we only have valid nodes
.map(node -> {
// try to parse each node and return either the result or the exception
try {
return parse(node);
} catch (ParseException e) {
return e;
}
}) // at this point we have stream of objects which may be either Result or ParseException
.collect(Collectors.partitioningBy(o -> o instanceof Result)); // split the stream into two lists - one containing Results, the other containing ParseExceptions
if (!collect.get(true).isEmpty()) {
return (Result) collect.get(true).get(0);
}
if (!collect.get(false).isEmpty()) {
throw (Exception) collect.get(false).get(0); // throws first exception instead of last!
}
return null;
}
Wie eingangs erwähnt, gibt es möglich, Leistungsproblem ist als dies versuchen wird alle gültigen Knoten zu analysieren.
EDIT:
Um zu vermeiden, alle Knoten Parsen, könnten Sie reduce
verwenden, aber es ist ein wenig komplexer und hässlich (und Extraklasse ist erforderlich). Dies zeigt auch alle ParseException
s statt nur die letzte.
private static class IntermediateResult {
private final SndNode node;
private final Result result;
private final List<ParseException> exceptions;
private IntermediateResult(SndNode node, Result result, List<ParseException> exceptions) {
this.node = node;
this.result = result;
this.exceptions = exceptions;
}
private Result getResult() throws ParseException {
if (result != null) {
return result;
}
if (exceptions.isEmpty()) {
return null;
}
// this will show all ParseExceptions instead of just last one
ParseException exception = new ParseException(String.format("None of %s valid nodes could be parsed", exceptions.size()));
exceptions.stream().forEach(exception::addSuppressed);
throw exception;
}
}
public static Result match(Response rsp) throws Exception {
return Stream.concat(
Arrays.stream(new SndNode[] {null}), // adding null at the beginning of the stream to get an empty "aggregatedResult" at the beginning of the stream
rsp.getFirstNodes().stream()
.flatMap(firstNode -> firstNode.getSndNodes().stream())
.filter(SndNode::isValid)
)
.map(node -> new IntermediateResult(node, null, Collections.<ParseException>emptyList()))
.reduce((aggregatedResult, next) -> {
if (aggregatedResult.result != null) {
return aggregatedResult;
}
try {
return new IntermediateResult(null, parse(next.node), null);
} catch (ParseException e) {
List<ParseException> exceptions = new ArrayList<>(aggregatedResult.exceptions);
exceptions.add(e);
return new IntermediateResult(null, null, Collections.unmodifiableList(exceptions));
}
})
.get() // aggregatedResult after going through the whole stream, there will always be at least one because we added one at the beginning
.getResult(); // return Result, null (if no valid nodes) or throw ParseException
}
EDIT2:
Im Allgemeinen ist es auch möglich faul Auswertung zu verwenden, wenn Terminalbetreiber wie findFirst()
verwenden. Mit einer geringfügigen Änderung der Anforderungen (d. H. Rückgabe von null anstelle des Auslösens einer Ausnahme) sollte es möglich sein, etwas wie unten zu tun. flatMap
mit findFirst
verwendet jedoch keine faule Auswertung (source), daher versucht dieser Code, alle Knoten zu analysieren.
private static class ParsedNode {
private final Result result;
private ParsedNode(Result result) {
this.result = result;
}
}
public static Result match(Response rsp) throws Exception {
return rsp.getFirstNodes().stream()
.flatMap(firstNode -> firstNode.getSndNodes().stream())
.filter(SndNode::isValid)
.map(node -> {
try {
// will parse all nodes because of flatMap
return new ParsedNode(parse(node));
} catch (ParseException e) {
return new ParsedNode(null);
}
})
.filter(parsedNode -> parsedNode.result != null)
.findFirst().orElse(new ParsedNode(null)).result;
}
Sie auf das erste Element direkt Rückkehr - 'Parse (sndNode);'. Nach welchen Kriterien wird mit dem nächsten Element fortgefahren? Was für ein Spiel macht es? –
Ich gebe das erste Element zurück, das einer Bedingung entspricht und keinen Analysefehler aufweist. – membersound
Ich würde die Liste mit 'flatMap()' abflachen, dann eine 'map()' -Operation anwenden, um das Parsing durchzuführen (null bei fehlgeschlagenem Erfolg), 'filter()' die Nullen und schließlich 'findFirst()' zurückzugeben der erste analysierte Wert Und ich würde das Werfen der letzten Ausnahme aufgeben (warum die letzte? Warum nicht die erste oder die zweite?), Da es sowieso nur Verwirrung stiftet. – biziclop