Non penso che ci sia un modo per aggirare la mappa. È tuttavia possibile utilizzare groupingBy
per nasconderlo, ma è effettivamente lo stesso codice e le stesse prestazioni suggerite da Paul Boddington.
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());
}
Non v'è alcuna mutazione della lista originale e si può facilmente cambiare il comportamento di fusione delle liste - per esempio se si vuole sbarazzarsi di duplicati basta aggiungere .distinct()
dopo flatMap(list -> list.numbers.stream())
(ricordate di aggiungere equals
a B
) o in modo simile è possibile ordinarli semplicemente aggiungendo .sorted()
(si deve fare in modo che B attui l'interfaccia Comparable
o usi semplicemente .sorted(Comparator<B>)
).
Qui è pieno di codice con i test e le importazioni:
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:
Seguendo le vostre domande nei commenti, se si desidera aggiungere più campi in groupingBy
clausola, si avrebbe bisogno di crea una classe che rappresenta una chiave nella mappa. Se hai campi che non vuoi includere nella chiave, devi definire come unire due valori diversi, in modo simile a ciò che fai con i numeri. A seconda di quali sono i campi, il comportamento di unione può essere semplicemente scegliere il primo valore e scartare l'altro (cosa ho fatto con numbers
nel codice qui sotto).
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 Sì Sto creando la nuova istanza di classe A con i valori del database e aggiungendola all'elenco. Una volta che la lista è popolata. Ho bisogno di ottenere oggetto distinto di classe A con l'elenco concatenato di classe B. –
Grazie. Ho cancellato il commento perché a prima vista sembrava che 'A' e' B' fossero pseudocodice ma poi si sono resi conto che sono classi complete che compilano. –