i am trying to test a Rest API PUT request. It is a spring boot application.
PUT request is used to do an update in the existing list of objects
traditional way of writing is working.
data is the data in the memory - which is a List<Bean> and
name (string type) is the key to find the object in the data and objectBean is the one to replace once we find with the key(that is name)
public void update(Bean objectBean, String name) {
for(int i = 0; i < data.size() ; i++) {
Bean l = data.get(i);
if(l.getName().equals(name)) {
data.set(i, objectBean);
return;
}
}
};
but i tried to write using Stream in java 8 . below is the code
Data.stream().map(p -> p.getName().equals(name) ? objectBean: p );
but this gives empty list.
Using streams here makes code only more complicated.
If you really wants you can introduce it to find the index i value. After that you can do the replacement.
IntStream.range(0, data.size())
.filter(i -> data.get(i).getName().equals(name)).findFirst()
.ifPresent(i -> data.set(i, objectBean));
Given that data is some List with Bean objects, you'd need to return your collected stream:
return data.stream()
.map(bean -> bean.getName().equals(name) ? objectBean : bean)
.collect(Collectors.toList());
If data is a non-empty Iterable then the output must be as well as map takes a Function object. However, this is not a good use case for the Stream API:
Firstly, streams are designed for side-effect-free purposes (i.e., creating new data structures rather updating them). The stream API supports forEach(Consumer<super T>) which is designed for side effects, but so do many other collections, in fact, all Iterables, whereas the immutable operations such as map and flatMap are not.
Second, I can't see the rest of your program, but at least in this snippet you seem to be updating your data structure based on the name, and you assume the name is unique because you stopped as soon as you reached the first Bean with the name you're looking for. Consider using Map<String, Bean> as your data structure.
Lastly, streams are lazy data structures, meaning that all the chained operations get computed when you collect. This provides incentive to chain a lot of computations together - chaining just a single map doesn't give you any performance advantages (tho it does give you referential transparency).
return data.stream()
.filter(bean -> bean.getName().equals(name))
.findAny()
Related
I have the following for loop:
List<Player> players = new ArrayList<>();
for (Team team : teams) {
ArrayList<TeamPlayer> teamPlayers = team.getTeamPlayers();
for (teamPlayer player : teamPlayers) {
players.add(new Player(player.getName, player.getPosition());
}
}
and I'm trying to convert it to a Stream:
List<Player> players = teams.forEach(t -> t.getTeamPlayers()
.forEach(p -> players.add(new Player(p.getName(), p.getPosition())))
);
But I'm getting a compilation error:
variable 'players' might not have been initialized
Why is this happening? Maybe there's an alternative way to create the stream, I was thinking of using flatMap but not sure how to apply it.
First of all, you need to understand that Streams don't act like Loops.
Hence, don't try to mimic a loop. Examine the tools offered by the API. Operation forEach() is there for special cases when you need to perform side-effects, not in order to accumulate elements from the stream into a Collection.
Note: with teams.forEach() you're not actually using a stream, but method Iterable.forEach() which is available with every implementation of Iterable.
To perform reduction on streams, we have several specialized operations like collect, reduce, etc. (for more information refer to the API documentation - Reduction).
collect() operation is meant to perform mutable reduction. You can use to collect the data into a list by providing built-in Collector Collectors.toList() as an argument. And since Java 16 operation toList() was introduced into API, which is implemented on top of the toArray() operation and performs better than namesake collector (therefore it's a preferred option if your JDK version allows you to use it).
I was thinking of using flatMap but not sure how to apply it.
Operation flatMap() is meant to perform one-to-many transformations. It expects a Function which takes a stream element and generates a Stream of the resulting type, elements of the generated stream become a replacement for the initial element.
Note: that general approach to writing streams to use as fewer operations as possible (because one of the main advantages that Functional programming brings to Java is simplicity). For that reason, applying flatMap() when a stream element produces a Collection in a single step is idiomatic, since it's sorter than performing map().flatMap() in two steps.
That's how implementation might look like:
List<Team> teams = List.of();
List<Player> players = teams.stream() // Stream<Team>
.flatMap(team -> team.getTeamPlayers().stream()) // Stream<Player>
.map(player -> new Player(player.getName(), player.getPosition()))
.toList(); // for Java 16+ or collect(Collectors.toList())
This is basically the answer of Alexander Ivanchenko, but with method reference.
final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(p -> new Player(p.getName(), p.getPosition()))
.toList();
If your Player class has a factory method like (depending on the relation between Player and TeamPlayer:
public static Player fromTeamPlayer(final TeamPlayer teamPlayer) {
return new Player(teamPlayer.getName(), teamPlayer.getPosition());
}
You could further reduce it to:
final var players = teams.stream()
.map(Team::getTeamPlayers)
.flatMap(Collection::stream)
.map(Player::fromTeamPlayer)
.toList();
Java stream on specific fields in a custom class object
I have an ArrayList of Train objects.
Each Train has three fields: source, destination, cost.
I want to get all the place names, i.e. all distinct sources + destinations.
I am using the below code, but as it can be observed, I'm using two streams to retrieve the data.
List<String> destinations = list.stream()
.map(x -> x.getDestination())
.distinct()
.collect(Collectors.toList());
List<String> sources = List.stream()
.map(x -> x.getSource())
.distinct()
.collect(Collectors.toList());
I was wondering how I could accomplish the same thing in a single stream? Can it be done using flatMap, or there's another way to achieve this?
List<String> allPlaces = ?
Also, is this possible to use Train class without getters?
You had the right idea with flatMap - you can map a train to a stream that contains the source and destination, and then flatMap it to you "main" stream:
List<String> allPlaces =
trains.stream()
.flatMap(t -> Stream.of(t.getSource(), t.getDestination()))
.distinct()
.collect(Collectors.toList());
In this case, we can utilize Java 16 method mapMulti(), which is functionally similar to flatMap(). It's meant for transforming a single stream element into a group of elements.
Here's how implementation might look like:
List<String> places = trains.stream()
.<String>mapMulti((train, consumer) -> {
consumer.accept(train.getSource());
consumer.accept(train.getDestination());
})
.distinct()
.toList();
Contrary to flatMap() it doesn't consume a stream, but operates via Consumer. mapMulti() a recommended alternative to flatMap() for situations when a new stream flatMap() requires would contain only a few elements (like in this case when we have only two elements: source and destination).
A quote from the API Note:
This method is preferable to flatMap in the following circumstances:
When replacing each stream element with a small (possibly zero)
number of elements. Using this method avoids the overhead of creating
a new Stream instance for every group of result elements, as required
by flatMap.
Addressing peripheral question:
Also is this possible without the getters methods of class Train?
Sure, you can. But it's not a recommended practice to access instance fields directly. In Java we're using access modifier to hide and protect member-variables within the class, that's one of the aspects of Encapsulation.
This is a bit of a fundamental question with an example project, as I'm still learning the best practices of Java 8 features.
Say I have an Order object, that cointains a List of OrderDetail. At the same time, OrderDetail contains a source and a destiny, along with quantity and product. OrderDetail also has an order (fk).
For this example, I will be moving Products from source to destiny, which both are ProductWarehouse objects with an availableStock property, which will be affected by the result of the Order.
Now, I need to update the availableStock for all the sources and destiny-ies. The stock should increase for destiny-ies and decrease for sources by the quantity of the OrderDetail. Since destiny and source are of the same type I can update them at the same time. The problem is that, of course, they are different properties.
My first thought was as follows:
//Map keeps record of the sum of products moved from the source. Key is the id the Product
HashMap<Integer, Double> quantities;
ArrayList<OrderDetail> orderDetails;
/**
* This could've been a single stream, but I divided it in two for readability here
* If not divided, the forEach() method from ArrayList<> might been enough though
*/
public void updateStock() {
List<ProductWarehouse> updated = new ArrayList<>();
orderDetails.stream()
.map(OrderDetail::getSource)
.forEach(src -> {
src.setAvailableStock(src.getAvailableStock - quantities.get(src.getProductId()));
updated.add(src);
});
orderDetails.stream()
.map(OrderDetail::getDestiny)
.forEach(dst -> {
dst.setAvailableStock(dst.getAvailableStock + quantities.get(dst.getProductId()));
updated.add(dst);
});
productWarehouseRepository.save(updated);
}
While this works, there is a problem: This is like the example in the Javadoc about "unnecesary side efects". It is not strictly the same, but given how similar they are makes me think I'm taking avoidable risks.
Another option I thought of is using peek() which has it's own implications since that method was thought mainly as a debugging tool, according to the javadoc.
HashMap<Integer, Double> quantities;
ArrayList<OrderDetail> orderDetails;
/**
* Either make it less readable and do one save or do one save for source and one for destiny
* I could also keep the updated list from before and addAll() the result of each .collect()
*/
public void updateStock() {
productWarehouseRepository.save(
orderDetails.stream()
.map(/*Same as above*/)
.peek(src -> {/*Same as above*/})
.collect(toList()) //static import
);
}
I've also read in a few blog post around that peek should be avoided. This, again, because the doc says what it's usage should be (mostly).
Hence the question: If I'd want to modify the properties of a Collection using the Stream API, what would be the best way to do so, following best-practices? For an example like this, is it better to use Iterable.forEach() or a simple enhanced for-loop? I honestly don't see where the side-effect might arise with operations like these using Stream, but that's probably due lack of experience and understanding of the API.
The code in this quesiton is just for example purposes, but if it's easier to understand with models or more info, I can add it.
You want to perform two different operations.
Update each ProductWarehouse object
Collect all ProductWarehouse objects into a list, for the save
Don’t mix the two operations.
public void updateStock() {
orderDetails.forEach(o -> {
ProductWarehouse src = o.getSource();
src.setAvailableStock(src.getAvailableStock()-quantities.get(src.getProductId()));
ProductWarehouse dst = o.getDestiny();
dst.setAvailableStock(dst.getAvailableStock()+quantities.get(dst.getProductId()));
});
List<ProductWarehouse> updated = orderDetails.stream()
.flatMap(o -> Stream.of(o.getSource(), o.getDestiny()))
.collect(Collectors.toList());
productWarehouseRepository.save(updated);
}
There is no point in trying to perform everything in a single stream operation at all costs. In the rare case where iterating the source twice is expense or not guaranteed to produce the same elements, you can iterate over the result list, e.g.
public void updateStock() {
List<ProductWarehouse> updated = orderDetails.stream()
.flatMap(o -> Stream.of(o.getSource(), o.getDestiny()))
.collect(Collectors.toList());
for(int ix = 0; ix < updated.size(); ) {
ProductWarehouse src = updated.get(ix++);
src.setAvailableStock(src.getAvailableStock()-quantities.get(src.getProductId()));
ProductWarehouse dst = updated.get(ix++);
dst.setAvailableStock(dst.getAvailableStock()+quantities.get(dst.getProductId()));
}
productWarehouseRepository.save(updated);
}
This second iteration is definitely cheap.
In both cases, forEach is a terminal action, so you should use map instead
If you want to avoid side-effects, then call your setter method within a map, then collect that to a list, which you add to the outer list.
updated.addAll(orderDetails.stream().map()...collect(Collectors.toList())
Related solution - How to add elements of a Java8 stream into an existing List
I have a List defined as follows:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
How can I increment each element of the List by one (i.e. end up with a List [2,3]) using Java 8's Stream API without creating new List?
When you create a Stream from the List, you are not allowed to modify the source List from the Stream as specified in the “Non-interference” section of the package documentation. Not obeying this constraint can result in a ConcurrentModificationException or, even worse, a corrupted data structure without getting an exception.
The only solution to directly manipulate the list using a Java Stream, is to create a Stream not iterating over the list itself, i.e. a stream iterating over the indices like
IntStream.range(0, list1.size()).forEach(ix -> list1.set(ix, list1.get(ix)+1));
like in Eran’s answer
But it’s not necessary to use a Stream here. The goal can be achieved as simple as
list1.replaceAll(i -> i + 1);
This is a new List method introduced in Java 8, also allowing to smoothly use a lambda expression. Besides that, there are also the probably well-known Iterable.forEach, the nice Collection.removeIf, and the in-place List.sort method, to name other new Collection operations not involving the Stream API. Also, the Map interface got several new methods worth knowing.
See also “New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8” from the official documentation.
Holger's answer is just about perfect. However, if you're concerned with integer overflow, then you can use another utility method that was released in Java 8: Math#incrementExact. This will throw an ArithmeticException if the result overflows an int. A method reference can be used for this as well, as seen below:
list1.replaceAll(Math::incrementExact);
You can iterate over the indices via an IntStream combined with forEach:
IntStream.range(0,list1.size()).forEach(i->list1.set(i,list1.get(i)+1));
However, this is not much different than a normal for loop, and probably less readable.
reassign the result to list1:
list1 = list1.stream().map(i -> i+1).collect(Collectors.toList());
public static Function<Map<String, LinkedList<Long>>, Map<String, LinkedList<Long>>> applyDiscount = (
objectOfMAp) -> {
objectOfMAp.values().forEach(listfLong -> {
LongStream.range(0, ((LinkedList<Long>) listfLong).size()).forEach(index -> {
Integer position = (int) index;
Double l = listfLong.get(position) - (10.0 / 100 * listfLong.get(position));
listfLong.set(position, l.longValue());
});
});
return objectOfMAp;
};
I'd like to duplicate a Java 8 stream so that I can deal with it twice. I can collect as a list and get new streams from that;
// doSomething() returns a stream
List<A> thing = doSomething().collect(toList());
thing.stream()... // do stuff
thing.stream()... // do other stuff
But I kind of think there should be a more efficient/elegant way.
Is there a way to copy the stream without turning it into a collection?
I'm actually working with a stream of Eithers, so want to process the left projection one way before moving onto the right projection and dealing with that another way. Kind of like this (which, so far, I'm forced to use the toList trick with).
List<Either<Pair<A, Throwable>, A>> results = doSomething().collect(toList());
Stream<Pair<A, Throwable>> failures = results.stream().flatMap(either -> either.left());
failures.forEach(failure -> ... );
Stream<A> successes = results.stream().flatMap(either -> either.right());
successes.forEach(success -> ... );
I think your assumption about efficiency is kind of backwards. You get this huge efficiency payback if you're only going to use the data once, because you don't have to store it, and streams give you powerful "loop fusion" optimizations that let you flow the whole data efficiently through the pipeline.
If you want to re-use the same data, then by definition you either have to generate it twice (deterministically) or store it. If it already happens to be in a collection, great; then iterating it twice is cheap.
We did experiment in the design with "forked streams". What we found was that supporting this had real costs; it burdened the common case (use once) at the expense of the uncommon case. The big problem was dealing with "what happens when the two pipelines don't consume data at the same rate." Now you're back to buffering anyway. This was a feature that clearly didn't carry its weight.
If you want to operate on the same data repeatedly, either store it, or structure your operations as Consumers and do the following:
stream()...stuff....forEach(e -> { consumerA(e); consumerB(e); });
You might also look into the RxJava library, as its processing model lends itself better to this kind of "stream forking".
You can use a local variable with a Supplier to set up common parts of the stream pipeline.
From http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/:
Reusing Streams
Java 8 streams cannot be reused. As soon as you call any terminal operation the stream is closed:
Stream<String> stream = Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
stream.anyMatch(s -> true); // ok
stream.noneMatch(s -> true); // exception
Calling `noneMatch` after `anyMatch` on the same stream results in the following exception:
java.lang.IllegalStateException: stream has already been operated upon or closed
at
java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at
java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
at com.winterbe.java8.Streams5.test7(Streams5.java:38)
at com.winterbe.java8.Streams5.main(Streams5.java:28)
To overcome this limitation we have to to create a new stream chain for every terminal operation we want to execute, e.g. we could create a stream supplier to construct a new stream with all intermediate operations already set up:
Supplier<Stream<String>> streamSupplier =
() -> Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> s.startsWith("a"));
streamSupplier.get().anyMatch(s -> true); // ok
streamSupplier.get().noneMatch(s -> true); // ok
Each call to get() constructs a new stream on which we are save to call the desired terminal operation.
Use a Supplier to produce the stream for each termination operation.
Supplier<Stream<Integer>> streamSupplier = () -> list.stream();
Whenever you need a stream of that collection,
use streamSupplier.get() to get a new stream.
Examples:
streamSupplier.get().anyMatch(predicate);
streamSupplier.get().allMatch(predicate2);
We've implemented a duplicate() method for streams in jOOλ, an Open Source library that we created to improve integration testing for jOOQ. Essentially, you can just write:
Tuple2<Seq<A>, Seq<A>> duplicates = Seq.seq(doSomething()).duplicate();
Internally, there is a buffer storing all values that have been consumed from one stream but not from the other. That's probably as efficient as it gets if your two streams are consumed about at the same rate, and if you can live with the lack of thread-safety.
Here's how the algorithm works:
static <T> Tuple2<Seq<T>, Seq<T>> duplicate(Stream<T> stream) {
final List<T> gap = new LinkedList<>();
final Iterator<T> it = stream.iterator();
#SuppressWarnings("unchecked")
final Iterator<T>[] ahead = new Iterator[] { null };
class Duplicate implements Iterator<T> {
#Override
public boolean hasNext() {
if (ahead[0] == null || ahead[0] == this)
return it.hasNext();
return !gap.isEmpty();
}
#Override
public T next() {
if (ahead[0] == null)
ahead[0] = this;
if (ahead[0] == this) {
T value = it.next();
gap.offer(value);
return value;
}
return gap.poll();
}
}
return tuple(seq(new Duplicate()), seq(new Duplicate()));
}
More source code here
Tuple2 is probably like your Pair type, whereas Seq is Stream with some enhancements.
You could create a stream of runnables (for example):
results.stream()
.flatMap(either -> Stream.<Runnable> of(
() -> failure(either.left()),
() -> success(either.right())))
.forEach(Runnable::run);
Where failure and success are the operations to apply. This will however create quite a few temporary objects and may not be more efficient than starting from a collection and streaming/iterating it twice.
Another way to handle the elements multiple times is to use Stream.peek(Consumer):
doSomething().stream()
.peek(either -> handleFailure(either.left()))
.foreach(either -> handleSuccess(either.right()));
peek(Consumer) can be chained as many times as needed.
doSomething().stream()
.peek(element -> handleFoo(element.foo()))
.peek(element -> handleBar(element.bar()))
.peek(element -> handleBaz(element.baz()))
.foreach(element-> handleQux(element.qux()));
cyclops-react, a library I contribute to, has a static method that will allow you duplicate a Stream (and returns a jOOλ Tuple of Streams).
Stream<Integer> stream = Stream.of(1,2,3);
Tuple2<Stream<Integer>,Stream<Integer>> streams = StreamUtils.duplicate(stream);
See comments, there is performance penalty that will be incurred when using duplicate on an existing Stream. A more performant alternative would be to use Streamable :-
There is also a (lazy) Streamable class that can be constructed from a Stream, Iterable or Array and replayed multiple times.
Streamable<Integer> streamable = Streamable.of(1,2,3);
streamable.stream().forEach(System.out::println);
streamable.stream().forEach(System.out::println);
AsStreamable.synchronizedFromStream(stream) - can be used to create a Streamable that will lazily populate it's backing collection, in a way such that can be shared across threads. Streamable.fromStream(stream) will not incur any synchronization overhead.
For this particular problem you can use also partitioning. Something like
// Partition Eighters into left and right
List<Either<Pair<A, Throwable>, A>> results = doSomething();
Map<Boolean, Object> passingFailing = results.collect(Collectors.partitioningBy(s -> s.isLeft()));
passingFailing.get(true) <- here will be all passing (left values)
passingFailing.get(false) <- here will be all failing (right values)
We can make use of Stream Builder at the time of reading or iterating a stream.
Here's the document of Stream Builder.
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.Builder.html
Use case
Let's say we have employee stream and we need to use this stream to write employee data in excel file and then update the employee collection/table
[This is just use case to show the use of Stream Builder]:
Stream.Builder<Employee> builder = Stream.builder();
employee.forEach( emp -> {
//store employee data to excel file
// and use the same object to build the stream.
builder.add(emp);
});
//Now this stream can be used to update the employee collection
Stream<Employee> newStream = builder.build();
I had a similar problem, and could think of three different intermediate structures from which to create a copy of the stream: a List, an array and a Stream.Builder. I wrote a little benchmark program, which suggested that from a performance point of view the List was about 30% slower than the other two which were fairly similar.
The only drawback of converting to an array is that it is tricky if your element type is a generic type (which in my case it was); therefore I prefer to use a Stream.Builder.
I ended up writing a little function that creates a Collector:
private static <T> Collector<T, Stream.Builder<T>, Stream<T>> copyCollector()
{
return Collector.of(Stream::builder, Stream.Builder::add, (b1, b2) -> {
b2.build().forEach(b1);
return b1;
}, Stream.Builder::build);
}
I can then make a copy of any stream str by doing str.collect(copyCollector()) which feels quite in keeping with the idiomatic usage of streams.