Reputation: 135
I would like to optimize the below code to take an existing ArrayList
and make a new list based on the below logic. Does streaming/lambda in Java 8 make it possible?
for (ShipmentTracingDTO tracing : tracings) {
if (tracing.getDestination()) {
newTracings.add(tracing);
break;
}
newTracings.add(tracing);
}
Upvotes: 8
Views: 264
Reputation: 32046
You can do it using IntStream.range
and List.subList
possibly as :
List<ShipmentTracingDTO> newTracings = tracings.subList(0, IntStream.range(0, tracings.size())
.filter(i -> tracings.get(i).getDestination())
.map(a -> a + 1) // to include the index of occurrence
.findFirst()
.orElse(tracings.size())); // else include all until last element
Note: This is just a view of tracings
and hence changing the underlying list tracings
would also reflect in changes in newTracings
. If you want to decouple these, you can create a new ArrayList<>()
from the solutions.
Edit: In comments from Holger, you could alternatively use:
List<ShipmentTracingDTO> newTracings = IntStream.rangeClosed(1, tracings.size())
.filter(i -> tracings.get(i - 1).getDestination()) // i-1 to include this as well
.mapToObj(i -> tracings.subList(0, i)) // Stream<List<ShipmentTracingDTO>>
.findFirst() // Optional<List<ShipmentTracingDTO>>
.orElse(tracings); // or else the entire list
Upvotes: 9
Reputation: 11664
First, I would point out there's not much wrong with the original code that you want to change. Just because Java has streams, you don't have to use them.
To use streams, I would break up the solution into two phases:
OptionalInt indexOfMatchingItem = IntStream.range(0, tracings.size())
.filter(i -> tracings.get(i).getDestination())
.findFirst();
List<ShipmentTracingDTO> newTracings = new ArrayList<>(
indexOfMatchingItem
.map(i -> tracings.subList(0, i + 1))
.orElse(tracings));
The above can be written as a single expression, but splitting it with a suitably named intermediate variable can make the code self-documenting.
I've created a new ArrayList so that it won't be affected by any subsequent changes to the original list tracings
. If tracings
is immutable, you can skip the construction of a new ArrayList.
The above solution is slightly better performing than the original code in the question, because the ArrayList constructor pre-allocates an array of exactly the required size, and so avoids the overhead of resizing and multiple array copies.
An alternative (perhaps more readable but less performant) solution is:
List<ShipmentTracingDTO> newTracings =
Stream.concat(
tracings.stream().takeWhile(i -> !tracings.get(i).getDestination()),
tracings.stream().dropWhile(i -> !tracings.get(i).getDestination()).limit(1)
).collect(toList());
Upvotes: 4
Reputation: 620
If I needed such processing I would divide it into two separate streams:
final int lastElemPosition = tracings.size() - 1;
final int firstElemWithGetDestinationTruePosition = IntStream.range(0, lastElemPosition)
.filter(i -> tracings.get(i).getDestination())
.findFirst()
.orElse(lastElemPosition);
final List<ShipmentTracingDTO> desiredElements = IntStream.rangeClosed(0, firstElemWithGetDestinationTruePosition)
.mapToObj(tracings::get)
.collect(Collectors.toList());
Upvotes: 4