I have something like the below :
public class MyClass {
private Long stackId
private Long questionId
}
A collection of say 100, where the
List<MyClass> inputs = Arrays.asList(
new MyClass(1L, 100L),
new MyClass(1L, 101L),
new MyClass(1L, 102L),
new MyClass(1L, 103L),
new MyClass(2L, 200L),
new MyClass(2L, 201L),
new MyClass(2L, 202L),
new MyClass(2L, 203L)
);
Map<Long, List<Long>> result = inputs
.stream()
.collect(
Collectors.groupingBy(MyClass::getStackId,
Collectors.mapping(
MyClass::getQuestionId,
Collectors.toList()
)
)
);
The straight-forward way with the Stream API involves 2 Stream pipelines:
Map<Long, List<Long>> of stackId to questionIds. This is done with the groupingBy(classifier, downstream) collectors where we classify per the stackId and values having the same stackId are mapped to their questionId (with mapping) and collected into a list with toList().MyOtherClass instance and collects that into a list.Assuming you have a constructor MyOtherClass(Long stackId, Collection<Long> questionIds), a sample code would be:
Map<Long, List<Long>> map =
list.stream()
.collect(Collectors.groupingBy(
MyClass::getStackId,
Collectors.mapping(MyClass::getQuestionId, Collectors.toList())
));
List<MyOtherClass> result =
map.entrySet()
.stream()
.map(e -> new MyOtherClass(e.getKey(), e.getValue()))
.collect(Collectors.toList());
Using StreamEx library, you could do that in a single Stream pipeline. This library offers a pairing and first collectors. This enables to pair two collectors and perform a finisher operation on the two collected results:
stackId of the grouped elements (they will all be the same, by construction)questionId and collecting into a list.MyOtherClass.Sample code:
import static java.util.stream.Collectors.collectingAndThen;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import static one.util.streamex.MoreCollectors.first;
import static one.util.streamex.MoreCollectors.pairing;
// ...
Collection<MyOtherClass> result =
StreamEx.of(list)
.groupingBy(
MyClass::getStackId,
pairing(
collectingAndThen(mapping(MyClass::getStackId, first()), Optional::get),
mapping(MyClass::getQuestionId, toList()),
MyOtherClass::new
)
).values();
You can use the java8 groupingBy collector. Like this:
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class RandomTest {
class MyClass {
private Long stackId;
private Long questionId;
public MyClass(Long stackId, Long questionId) {
this.stackId = stackId;
this.questionId = questionId;
}
public Long getStackId() {
return stackId;
}
public Long getQuestionId() {
return questionId;
}
}
public class MyOtherClass {
private Long stackId;
private Set<Long> questionIds;
public MyOtherClass(Long stackId, Set<Long> questionIds) {
this.stackId = stackId;
this.questionIds = questionIds;
}
public Long getStackId() {
return stackId;
}
public Set<Long> getQuestionIds() {
return questionIds;
}
}
@Test
public void test() {
List<MyClass> classes = new ArrayList<>();
List<MyOtherClass> otherClasses = new ArrayList<>();
//populate the classes list
for (int j = 1; j <= 25; j++) {
for (int i = 0; i < 4; i++) {
classes.add(new MyClass(0L + j, (100L*j) + i));
}
}
//populate the otherClasses List
classes.stream().collect(Collectors
.groupingBy(MyClass::getStackId, Collectors.mapping(MyClass::getQuestionId, Collectors.toSet())))
.entrySet().stream().forEach(
longSetEntry -> otherClasses.add(new MyOtherClass(longSetEntry.getKey(), longSetEntry.getValue())));
//print the otherClasses list
otherClasses.forEach(myOtherClass -> {
System.out.print(myOtherClass.getStackId() + ": [");
myOtherClass.getQuestionIds().forEach(questionId-> System.out.print(questionId + ","));
System.out.println("]");
});
}
}