Ich habe eingehende Objekte mit einer flachen de-normalisierte Struktur, die ich von einem JDBC-Ergebnis instanziiert. Die eingehenden Objekte spiegeln die resultset gibt Lasten von wiederholten Daten ist, damit ich die Daten in eine Liste der übergeordneten Objekte mit verschachtelten Kind Sammlungen, dh ein Objektgraph oder normalisierte Liste konvertieren möchten.
Die eingehenden Objektklasse sieht wie folgt aus:
class IncomingFlatItem {
String clientCode;
String clientName;
String emailAddress;
boolean emailHtml;
String reportCode;
String reportLanguage;
}
So ist die eingehenden Daten enthält, die für jeden Kunden mehrere Objekte, die Ich mag würde zu einem Client-Objekt aggregieren, die eine Liste von E-Mail-Adresse Objekte für den Client enthält, und eine Liste der Berichtsobjekte.
So ist das Client-Objekt würde wie folgt aussehen:
class Client {
String clientCode;
String clientName;
Set<EmailAddress> emailAddresses;
Set<Report> reports;
}
Merkwürdiger Ich kann nicht eine vorhandene Antwort auf diese finden. Ich bin auf der Suche Ströme nisten oder Bäche Verkettungs aber ich möchte das eleganteste Ansatz finden, und ich möchte auf jeden Fall eine for-Schleife zu vermeiden.
Vielen Dank an alle Antworter , die erwähnt Collectors.groupingBy()
. Dies war der Schlüssel , um einen Strom der Einrichtung , wo ich verwenden könnte reduce()
. Ich hatte geglaubt , fälschlicherweise soll ich in der Lage sein zu verwenden , reduce
auf seinem eigenen , das Problem zu lösen, ohne groupingBy
.
Unser Dank gilt auch den Vorschlag , eine fließend API zu erstellen. Ich fügte hinzu , IncomingFlatItem.getEmailAddress()
und IncomingFlatItem.getReport()
zu fließend die Domänenobjekte aus greifen IncomingFlatItem
- und auch eine Methode , um das ganze Wohnung Element an ein richtiges Domain - Objekt mit seiner E - Mail und Bericht bereits verschachtelt zu konvertieren:
public Client getClient() {
Client client = new Client();
client.setClientCode(clientCode);
client.setClientName(clientName);
client.setEmailAddresses(new ArrayList());
client.getEmailAddresses().add(this.getEmailAddress());
client.setReports(new ArrayList<>());
client.getReports().add(this.getReport());
return client;
}
Ich habe auch Business - ID-basierte .equals()
und .hashCode()
Methoden auf Client
, EmailAddress
und Report
wie empfohlen von @SamuelPhilip
Schließlich für die Domain - Objekte, habe ich .addReport(Report r)
und .addEmail(EmailAddress e)
meine Client
Klasse, die das untergeordnete Objekt hinzufügen würde , Client
wenn nicht bereits vorhanden ist . Ich ditched den Set
Kollektionstyp für , List
weil der Domain - Modell Standard ist List
und Sets
würde viele Conversions hat gemeint Lists
.
Also mit diesem, schauen der Stream-Code und Lambda-Ausdrücke prägnant.
Es gibt drei Stufen:
- Karte
IncomingFlatItems
zuClients
- die Gruppe
Clients
in eine Karte durch den Kunden (die jedoch stark vonClient.equals()
) - reduzieren, jede Gruppe zu einem
Client
Das ist also der Funktionsalgorithmus:
List<Client> unflatten(List<IncomingFlatItem> flatItems) {
return flatItems.parallelStream()
.map(IncomingFlatItem::getClient)
.collect(Collectors.groupingByConcurrent(client -> client))
.entrySet().parallelStream()
.map(kvp -> kvp.getValue()
.stream()
.reduce(new Client(),
(client1, client2) -> {
client1.getReports()
.forEach(client2::addReport);
client1.getEmailAddresses()
.forEach(client2::addEmail);
return client2;
}))
.collect(Collectors.toList());
}
Ich habe eine lange Zeit wegen auf einer Tangente gehen, bevor ich wirklich verstanden reduce
- ich eine Lösung gefunden , die meine Tests bestanden während der Verwendung , .stream()
aber völlig gescheiterten .parallelStream()
daher seine Verwendung hier. Ich hatte zu verwenden , CopyOnWriteArrayList
wie auch sonst ist es zufällig umfallen würde mitConcurrentModificationExceptions