Ich glaube nicht, dass es einen Weg gibt, um die Karte zu umgehen. Sie können jedoch groupingBy
verwenden, um es zu verbergen, aber es ist effektiv der gleiche Code und die gleiche Leistung wie Paul Boddington vorgeschlagen. keine Mutation der ursprünglichen Liste
List<A> merge(List<A> input) {
return input.stream()
.collect(groupingBy(a -> a.name)) // map created here
.entrySet()
.stream()
.map(entry -> new A(
entry.getKey(),
entry.getValue().stream()
.flatMap(list -> list.numbers.stream())
.collect(toList()) // merging behaviour
)).collect(toList());
}
Es gibt und man kann leicht das Verhalten ändern, um die Listen der Zusammenführung - zum Beispiel, wenn Sie Duplikate loswerden wollen fügen Sie einfach .distinct()
nach flatMap(list -> list.numbers.stream())
(Sie erinnern sich über equals
zu B
Zugabe) oder Auf ähnliche Weise können Sie sie sortieren, indem Sie einfach .sorted()
hinzufügen (Sie müssen B implementieren Comparable
Schnittstelle oder einfach .sorted(Comparator<B>)
verwenden).
Hier vollständigen Code mit Tests und Importen ist:
import org.junit.Test;
import java.util.List;
import static com.shazam.shazamcrest.MatcherAssert.assertThat;
import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
import static java.util.Arrays.asList;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
public class Main {
class A {
final String name;
final List<B> numbers;
A(String name, List<B> numbers) {
this.name = name;
this.numbers = numbers;
}
}
class B {
final Integer number;
B(Integer number) {
this.number = number;
}
}
List<A> merge(List<A> input) {
return input.stream()
.collect(groupingBy(a -> a.name))
.entrySet()
.stream()
.map(entry -> new A(
entry.getKey(),
entry.getValue().stream()
.flatMap(list -> list.numbers.stream())
.collect(toList())
)).collect(toList());
}
@Test
public void test() {
List<A> input = asList(
new A("abc", asList(new B(1), new B(2))),
new A("xyz", asList(new B(3), new B(4))),
new A("abc", asList(new B(3), new B(5)))
);
List<A> list = merge(input);
assertThat(list, sameBeanAs(asList(
new A("abc", asList(new B(1), new B(2), new B(3), new B(5))),
new A("xyz", asList(new B(3), new B(4)))
)));
}
}
EDIT:
Ihre Fragen in den Kommentaren Folgen, wenn Sie mehrere Felder in groupingBy
Klausel hinzufügen möchten, müssen Sie Erstellen Sie eine Klasse, die einen Schlüssel in der Karte darstellt. Wenn Sie Felder haben, die Sie nicht in den Schlüssel aufnehmen möchten, müssen Sie definieren, wie zwei verschiedene Werte zusammengeführt werden - ähnlich wie bei Zahlen. Je nachdem, was die Felder sind, kann das Merging-Verhalten nur den ersten Wert auswählen und den anderen verwerfen (was ich im folgenden Code mit numbers
gemacht habe).
class A {
final String name;
final String type;
final List<B> numbers;
A(String name, String type, List<B> numbers) {
this.name = name;
this.type = type;
this.numbers = numbers;
}
}
class B {
final Integer number;
B(Integer number) {
this.number = number;
}
}
class Group {
final String name;
final String type;
Group(String name, String type) {
this.name = name;
this.type = type;
}
// this needs to have equals and hashCode methods as we use it as a key in a map
}
List<A> merge(List<A> input) {
return input.stream()
.collect(groupingBy(a -> new Group(a.name, a.type)))
.entrySet()
.stream()
.map(entry -> new A(
entry.getKey().name, // entry.getKey() is now Group, not String
entry.getKey().type,
entry.getValue().get(0).numbers // no merging, just take first
)).collect(toList());
}
@PaulBoddington Ja Ich erstelle die neue Klasse A-Instanz mit den Werten aus der Datenbank und füge sie in die Liste ein. Sobald die Liste ausgefüllt ist. Ich brauche distinct Objekt der Klasse A mit verketteten Liste der Klasse B. –
Danke. Ich habe den Kommentar gelöscht, weil es auf den ersten Blick so aussah, als wären 'A' und' B' Pseudocode, aber dann wurde klar, dass es sich um vollständige Klassen handelt, die kompilieren. –