问题
I have a class which contains different nested collections, now I want to receive all the elements of the nested collections, concrete I want to collect all the StrokePoints of the collections. I can solve it with "old" java but how to do it with streams?
int strokesCounter = 0;
List<StrokePoint> pointList = new ArrayList<>();
if (!strokesData.getListOfSessions().isEmpty()) {
for (SessionStrokes session : strokesData.getListOfSessions()) {
List<Strokes> strokes = session.getListOfStrokes();
for (Strokes stroke : strokes) {
strokesCounter++;
List<StrokePoint> points = stroke.getListOfStrokePoints();
pointList.addAll(stroke.getListOfStrokePoints());
}
}
}
I am looking for a way to fill the pointList with the stream functionality.
回答1:
Flattening the nested data is pretty simple:
List<StrokePoint> pointList = strokesData.getListOfSessions()
.streams()
.map(SessionStrokes::getListOfStrokes)
.flatMap(List::stream)
.map(Strokes::getListOfStrokePoints)
.flatMap(List::stream)
.collect(Collectors.toList());
Collecting the stroke count along the way is more tricky, and somewhat controversial. You could create a
AtomicInteger strokesCounter = new AtomicInteger();
and increment it just after the first flatMap
:
.peek(strokesCounter::incrementAndGet)
回答2:
You can just use Stream.flatMap() twice:
List<StrokePoint> pointList = strokesData.getListOfSessions().stream()
.flatMap(session -> session.getListOfStrokes().stream())
.flatMap(strokes -> strokes.getListOfStrokePoints().stream())
.collect(Collectors.toList());
If you need to count the strokes list you can split this into two parts and use List.size()
:
List<Strokes> strokesList = strokesData.getListOfSessions().stream()
.flatMap(session -> session.getListOfStrokes().stream())
.collect(Collectors.toList());
int strokesCounter = strokesList.size();
List<StrokePoint> pointList = strokesList.stream()
.flatMap(strokes -> strokes.getListOfStrokePoints().stream())
.collect(Collectors.toList());
Alternatively you can increment an AtomicInteger
in flatMap()
:
final AtomicInteger strokesCounter = new AtomicInteger();
List<StrokePoint> pointList = strokesData.getListOfSessions().stream()
.flatMap(session -> {
List<Strokes> strokes = session.getListOfStrokes();
strokesCounter.addAndGet(strokes.size());
return strokes.stream();
})
.flatMap(strokes -> strokes.getListOfStrokePoints().stream())
.collect(Collectors.toList());
Or with peek()
:
final AtomicInteger strokesCounter = new AtomicInteger();
List<StrokePoint> pointList = strokesData.getListOfSessions().stream()
.flatMap(session -> session.getListOfStrokes().stream())
.peek(i -> strokesCounter.incrementAndGet())
.flatMap(strokes -> strokes.getListOfStrokePoints().stream())
.collect(Collectors.toList());
回答3:
Since the primary aim is to solve for collecting the List<StrokePoint>
, you can perform it using the flatMap
operation as:
List<StrokePoint> points = strokesData.getListOfSessions()
.stream()
.flatMap(ss -> ss.getListOfStrokes().stream()
.flatMap(s -> s.getListOfStrokePoints().stream()))
.collect(Collectors.toList());
Alongside, the count of Stroke
s can also be evaluated using the streams by summing the size of the lists as:
long strokeCount = strokesData.getListOfSessions()
.stream()
.mapToLong(ss -> ss.getListOfStrokes().size())
.sum();
To merge these operations, you can construct a AbstractMap.SimpleEntry
while reducing the entries as:
AbstractMap.SimpleEntry<Integer, Stream<StrokePoint>> reduce = strokesData.getListOfSessions()
.stream()
.map(ss -> new AbstractMap.SimpleEntry<>(ss.getListOfStrokes().size(),
ss.getListOfStrokes()
.stream()
.flatMap(s -> s.getListOfStrokePoints().stream())))
.reduce(new AbstractMap.SimpleEntry<>(1, Stream.empty()),
(e1, e2) -> new AbstractMap.SimpleEntry<>(
Integer.sum(e1.getKey(), e2.getKey()),
Stream.concat(e1.getValue(), e2.getValue())));
Using this entry, you can attain the count of Stroke
s and the list of StrokePoint
s as :
long strokeCount = reduce.getKey();
List<StrokePoint> strokePoints = reduce.getValue().collect(Collectors.toList());
回答4:
If you're concerned about side-effects, this will do it (but both the other answers at the time of writing are infinitely more readable):
Entry<Integer, List<StrokePoint>> summary = //
strokesData.getListOfSessions()
.stream()
.flatMap(session -> session.getListOfStrokes().stream())
.map(strokes -> new SimpleEntry<>(1, strokes.getListOfStrokePoints()))
.reduce((l1, l2) -> {
int count = l1.getKey() + l2.getKey();
List<StrokePoint> list = l1.getValue();
list.addAll(l2.getValue());
return new SimpleEntry<>(count, list);
})
.orElse(new SimpleEntry<>(0, Collections.emptyList()));
strokesCounter = summary.getKey();
pointList = summary.getValue();
EDITING to add validation:
public class Scratch {
public static void main(String[] args) {
int strokesCounter = 0;
List<StrokePoint> pointList = new ArrayList<>();
StrokesData strokesData = new StrokesData();
SessionStrokes sessionStrokes = new SessionStrokes();
strokesData.sessionStrokes.add(sessionStrokes);
Strokes s1 = new Strokes();
sessionStrokes.strokesList.add(s1);
s1.strokePoints.add(new StrokePoint());
s1.strokePoints.add(new StrokePoint());
Strokes s2 = new Strokes();
sessionStrokes.strokesList.add(s2);
s2.strokePoints.add(new StrokePoint());
s2.strokePoints.add(new StrokePoint());
s2.strokePoints.add(new StrokePoint());
s2.strokePoints.add(new StrokePoint());
s2.strokePoints.add(new StrokePoint());
s2.strokePoints.add(new StrokePoint());
Entry<Integer, List<StrokePoint>> summary = //
strokesData.getListOfSessions()
.stream()
.flatMap(session -> session.getListOfStrokes()
.stream())
.map(strokes -> new SimpleEntry<>(1, strokes.getListOfStrokePoints()))
.reduce((l1, l2) -> {
int count = l1.getKey() + l2.getKey();
List<StrokePoint> list = l1.getValue();
list.addAll(l2.getValue());
return new SimpleEntry<>(count, list);
})
.orElse(new SimpleEntry<>(0, Collections.emptyList()));
strokesCounter = summary.getKey();
pointList = summary.getValue();
System.out.println(strokesCounter);
System.out.println(pointList);
}
}
class StrokesData {
List<SessionStrokes> sessionStrokes = new ArrayList<>();
public List<SessionStrokes> getListOfSessions() {
return sessionStrokes;
}
}
class SessionStrokes {
List<Strokes> strokesList = new ArrayList<>();
public List<Strokes> getListOfStrokes() {
return strokesList;
}
}
class Strokes {
List<StrokePoint> strokePoints = new ArrayList<>();
public List<StrokePoint> getListOfStrokePoints() {
return strokePoints;
}
}
class StrokePoint {
}
来源:https://stackoverflow.com/questions/56402684/how-to-get-all-elements-in-nested-collection-with-streams