How do I replace the Supplier code here with lambda expression
IntStream inStream = Stream.generate(new Supplier<Integer>() {
int x= 1;
#Override
public Integer get() {
return x++ ;
}
}).limit(10).mapToInt(t -> t.intValue());
inStream.forEach(System.out::println);
The output of above piece of code is:
1
2
3
4
5
6
7
8
9
10
The Stream::generate is not suitable for this issue. According to the documentation:
This is suitable for generating constant streams, streams of random elements, etc.
You might want to use rather IntStream::range:
IntStream intStream = IntStream.range(1, 11);
intStream.forEach(System.out::println);
Another solution might be using the IntStream.iterate where you can control the increment comfortably using the IntUnaryOperator:
IntStream intStream = IntStream.iterate(1, i -> i+1).limit(10);
intStream.forEach(System.out::println);
As said, the Stream::generate is suitable for the constant streams or random elements. The random element might be obtained using the class Random so here you might want to get an increment using AtomicInteger:
AtomicInteger atomicInteger = new AtomicInteger(1);
IntStream intStream = Stream.generate(atomicInteger::getAndIncrement).limit(10);
intStream.forEach(System.out::println);
Or you can use this
IntStream.iterate(1, i -> i + 1)
.limit(10)
.forEach(System.out::println);
Something like this if you're bound to use Stream.generate specifically :
IntStream inStream = Stream.generate(new AtomicInteger(1)::getAndIncrement)
.limit(10)
.mapToInt(t -> t);
inStream.forEach(System.out::println);
Edit: Using IntStream.generate, you can perform it as
IntStream.generate(new AtomicInteger(1)::getAndIncrement).limit(10);
Note: A better solution in terms of the API design would definitely be to make use of Stream.iterate for such a use case.
Related
The naive code I have is:
class ${
public static void main(String[] _) {
final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> i++).limit(5).forEach(val -> ints.add(val));
System.out.println(ints);
}
}
where my expectation was to see the following in the console:
[0, 1, 2, 3, 4]
But the actual is:
[0, 0, 0, 0, 0]
It is probably something very simple, but what am I missing?
i++ has the value of i before it's incremented. You need to use the prefix operator.
IntStream.iterate(0, i -> ++i).limit(5).forEach(val -> ints.add(val));
Actually, don't do that. There's no reason to mutate i, it's simply thrown away. Use the side effect free version. That's the whole idea behind functional programming, after all: avoiding side effects.
IntStream.iterate(0, i -> i + 1).limit(5).forEach(val -> ints.add(val));
For the specific case of a stream of consecutive integers you could replace iterate and limit with range:
IntStream.range(0, 5).forEach(val -> ints.add(val));
And finally, it would also be better to collect the stream into a list rather than adding values with forEach. It directly expresses the intention to create a list, which again avoids side effects.
List<Integer> ints = IntStream.range(0, 5).boxed().collect(Collectors.toList());
You are using a postfix i++, instead of a prefix ++i while passing on the value to forEach. Changing to the following shall provide you the expected output :
IntStream.iterate(0, i -> i + 1).limit(5).forEach(ints::add);
Aside, an alternate way of iterating and combining limit with Java9+ is using IntStream.iterate with an IntPredicate as :
IntStream.iterate(0, i -> i < 5, i -> i + 1).forEach(ints::add);
You need to return the incremented value. You postfix incremented a local variable and returned the non-incremented value. Use ++i not i++
final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> ++i).limit(5).forEach(val -> ints.add(val));
System.out.println(ints);
Edit See John Kugelman's post about using non-mutating operations when programming in a functional style. Using i + 1 will create a new primitive and not mutate the parameter variable.
You could use a print to see what was happend:
final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> {
System.out.println(i);
return i++;
}).limit(5)
.forEach(val -> ints.add(val));
System.out.println(ints);
In that case, the value of i always will be 0, because the increment occurs after the value is returned, the correct way is
final List<Integer> ints = new ArrayList<>();
IntStream.iterate(0, i -> {
System.out.println(i);
return ++i;
}).limit(5)
.forEach(val -> ints.add(val));
System.out.println(ints);
I have a set of constant values that are available as a list. Using these values I have to create a key value pair object and this object has to be added to a list. I would like to achieve this using Stream API in JAVA 8. Below is the sample implementation using a for loop
for (int i=0; i<length; i+=2){
list.add(new sampleObject(constant[i],constant[i+1]);
}
Can this be implemented using Stream reduction operations?
Chain IntStream.iterate() that produces a infinite IntStream with IntStream.limit() to make it finite :
List<sampleObject> list =
IntStream.iterate(0, i -> i + 2)
.limit(Math.ceil(length / 2D))
.mapToObj(i -> new sampleObject(constant[i], constant[i+1]))
.collect(Collectors.toList());
Of course it can!
IntStream.iterate(0, i -> i < length, i -> i + 2)
.mapToObj(i -> new sampleObject(constant[i], constant[i+1]))
.collect(Collectors.toList());
I'm not sure off the top of my head, but constant may have to be final or effectively final for this to compile.
Note: I just realized, this overloaded iterate method was added in Java 9. Please see davidxxx's answer for a Java 8 solution!
I'd like to perform such action using streams:
List<String> listX = new ArrayList<>();
for(int i=0;i<listY.size();i++){
listX.add(String.format(ABC,i));
}
so I have to iterate over a list using the length of another list and additionally use counter i. After all i'd like to return the new list
using IntStream.range & IntStream.mapToObj methods. and don't operates variables out of lambda expression in stream api, that will resulting in Thread Interference or Memory Consistency Errors in parallel stream.
List<String> listX = IntStream.range(0, listY.size())
.mapToObj(i -> String.format(ABC,i))
.collect(Collectors.toList());
You can use IntStream to iterate using index of listY as shown below:
IntStream.range(0, listY.size()).forEach(i -> listX.add(String.format(ABC,i)));
You could also use Collectors.mapping, something like this:
IntStream.range(0, listY.size())
.collect(Collectors.mapping(i -> String.format(ABC,i),
Collectors.toList()));
This question already has answers here:
Copy a stream to avoid "stream has already been operated upon or closed"
(10 answers)
Closed 3 years ago.
I'm learning the new Java 8 features, and while experimenting with streams (java.util.stream.Stream) and collectors, I realized that a stream can't be used twice.
Is there any way to reuse it?
If you want to have the effect of reusing a stream, you might wrap the stream expression in a Supplier and call myStreamSupplier.get() whenever you want a fresh one. For example:
Supplier<Stream<String>> sup = () -> someList.stream();
List<String> nonEmptyStrings = sup.get().filter(s -> !s.isEmpty()).collect(Collectors.toList());
Set<String> uniqueStrings = sup.get().collect(Collectors.toSet());
From the documentation:
A stream should be operated on (invoking an intermediate or terminal stream operation) only once.
A stream implementation may throw IllegalStateException if it detects that the stream is being reused.
So the answer is no, streams are not meant to be reused.
As others have said, "no you can't".
But it's useful to remember the handy summaryStatistics() for many basic operations:
So instead of:
List<Person> personList = getPersons();
personList.stream().mapToInt(p -> p.getAge()).average().getAsDouble();
personList.stream().mapToInt(p -> p.getAge()).min().getAsInt();
personList.stream().mapToInt(p -> p.getAge()).max().getAsInt();
You can:
// Can also be DoubleSummaryStatistics from mapToDouble()
IntSummaryStatistics stats = personList.stream()
.mapToInt(p-> p.getAge())
.summaryStatistics();
stats.getAverage();
stats.getMin();
stats.getMax();
The whole idea of the Stream is that it's once-off. This allows you to create non-reenterable sources (for example, reading the lines from the network connection) without intermediate storage. If you, however, want to reuse the Stream content, you may dump it into the intermediate collection to get the "hard copy":
Stream<MyType> stream = // get the stream from somewhere
List<MyType> list = stream.collect(Collectors.toList()); // materialize the stream contents
list.stream().doSomething // create a new stream from the list
list.stream().doSomethingElse // create one more stream from the list
If you don't want to materialize the stream, in some cases there are ways to do several things with the same stream at once. For example, you may refer to this or this question for details.
As others have noted the stream object itself cannot be reused.
But one way to get the effect of reusing a stream is to extract the stream creation code to a function.
You can do this by creating a method or a function object which contains the stream creation code. You can then use it multiple times.
Example:
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// The normal way to use a stream:
List<String> result1 = list.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
// The stream operation can be extracted to a local function to
// be reused on multiple sources:
Function<List<Integer>, List<String>> listOperation = l -> l.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
List<String> result2 = listOperation.apply(list);
List<String> result3 = listOperation.apply(Arrays.asList(1, 2, 3));
// Or the stream operation can be extracted to a static method,
// if it doesn't refer to any local variables:
List<String> result4 = streamMethod(list);
// The stream operation can also have Stream as argument and return value,
// so that it can be used as a component of a longer stream pipeline:
Function<Stream<Integer>, Stream<String>> streamOperation = s -> s
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i);
List<String> result5 = streamOperation.apply(list.stream().map(i -> i * 2))
.filter(s -> s.length() < 7)
.sorted()
.collect(toCollection(LinkedList::new));
}
public static List<String> streamMethod(List<Integer> l) {
return l.stream()
.filter(i -> i % 2 == 1)
.map(i -> i * i)
.limit(10)
.map(i -> "i :" + i)
.collect(toList());
}
If, on the other hand, you already have a stream object which you want to iterate over multiple times, then you must save the content of the stream in some collection object.
You can then get multiple streams with the same content from than collection.
Example:
public void test(Stream<Integer> stream) {
// Create a copy of the stream elements
List<Integer> streamCopy = stream.collect(toList());
// Use the copy to get multiple streams
List<Integer> result1 = streamCopy.stream() ...
List<Integer> result2 = streamCopy.stream() ...
}
Come to think of it, this will of "reusing" a stream is just the will of carry out the desired result with a nice inline operation. So, basically, what we're talking about here, is what can we do to keep on processing after we wrote a terminal operation?
1) if your terminal operation returns a collection, the problem is solved right away, since every collection can be turned back into a stream (JDK 8).
List<Integer> l=Arrays.asList(5,10,14);
l.stream()
.filter(nth-> nth>5)
.collect(Collectors.toList())
.stream()
.filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));
2) if your terminal operations returns an optional, with JDK 9 enhancements to Optional class, you can turn the Optional result into a stream, and obtain the desired nice inline operation:
List<Integer> l=Arrays.asList(5,10,14);
l.stream()
.filter(nth-> nth>5)
.findAny()
.stream()
.filter(nth-> nth%2==0).forEach(nth-> System.out.println(nth));
3) if your terminal operation returns something else, i really doubt that you should consider a stream to process such result:
List<Integer> l=Arrays.asList(5,10,14);
boolean allEven=l.stream()
.filter(nth-> nth>5)
.allMatch(nth-> nth%2==0);
if(allEven){
...
}
The Functional Java library provides its own streams that do what you are asking for, i.e. they're memoized and lazy. You can use its conversion methods to convert between Java SDK objects and FJ objects, e.g. Java8.JavaStream_Stream(stream) will return a reusable FJ stream given a JDK 8 stream.
I defined natural for Infinite sequence (Stream) of Natural numbers with Java8 iterator.
IntStream natural = IntStream.iterate(0, i -> i + 1);
natural
.limit(10)
.forEach(System.out::println);
Now, I want to define it with Java8 generator.
static Stream generate(Supplier s)
What would be the simplest way? Thanks.
With a generator you need to keep track of your current index. One way would be:
IntStream natural = IntStream.generate(new AtomicInteger()::getAndIncrement);
Note: I use AtomicInteger as a mutable integer rather than for its thread safety: if you parallelise the stream the order will not be as expected.
This is built into IntStream:
IntStream.range(0, Integer.MAX_VALUE)
This returns all values up to (but not including) Integer.MAX_VALUE.
Note: #assylias managed to do it with a lambda using AtomicInteger. He should probably have the accepted answer.
I'm not sure you can do that with a lambda (because it is stateful), but with a plain Supplier this would work:
IntSupplier generator = new IntSupplier() {
int current = 0;
public int getAsInt() {
return current++;
}
};
IntStream natural = IntStream.generate(generator);
However, I highly prefer your current solution, because this is the purpose of iterate(int seed, IntUnaryOperator f) IMHO:
IntStream natural = IntStream.iterate(0, i -> i + 1);