What are the use cases for IntStream? - java

I've written some code that creates a List<Integer> from a Stream<List<Integer>> like so:
List<Integer> abc = stream.flatMap(e -> e.stream()).
collect(Collectors.toList());
But I'm interested how to use IntStream. I thought I could do
List<Integer> abc = stream.flatMapToInt(e -> e.stream()).
collect(Collectors.toList());
using flatMapToInt to give me an IntStream that the collector would collect, but I get a compilation error:
Type mismatch: cannot convert from Collector<Object,?,List<Object>> to Supplier<R>
What is an IntStream, how is it different to a regular stream, and when would you use it?

You more-or-less use IntStream when you have intermediate or terminal operations that don't require boxing, and frequently when you want arithmetic results. For example, you might write
students.stream().mapToInt(Student::getTestScore).average()
Generally, you'd want to use it when your intermediate results are not boxed -- not the case with a List<List<Integer>> and either you're mapping the unboxed result to a boxed thing with mapToObj, or doing something arithmeticky with it like average() here.
It doesn't buy you anything except possibly pain for this use case, which doesn't actually care that the list contents are Integers; you're not using anything about integers here.

Related

Mapping over a list in Java

In Java 8 I can map over streams with the map method, e.g.
Stream.of("Hello", "world").map(s -> s.length())
gives me a stream containing the integers [5, 5]. I am trying to do the same with lists. I have come up with
List<String> list = ...
list.stream().map(s -> s.length()).collect(Collectors.toList())
This works but is rather verbose. Is there a more concise solution? Ideally, there would be a similar map method for lists, but I haven't found any. So, are there any alternatives?
As compact as possible
Just wrap it into your own utility function:
public <T, S> List<S> mapBy(List<T> items, Function<T, S> mapFn) {
return items.stream().map(mapFn).collect(Collectors.toList());
}
Now you can just use mapBy(students, Student::getName). It doesn't get less verbose than that.
Note that this is only useful if that's the only data mutation you want to make. Once you have more stream operators you want to apply it'd be better to do just that as otherwise you keep creating intermediate lists, which is quite wasteful.
Think practically to do operation on each element in list you need to either stream it or loop it, so stream is more concise than loop. for more info you can replace lambda expression with method reference operator
list.stream().map(String::length).collect(Collectors.toList());

How to update each element in a List in Java 8 using Stream API

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;
};

How can I sort an IntStream in ascending order?

I've converted a 2D int array into a Stream:
IntStream dataStream = Arrays.stream(data).flatMapToInt(x -> Arrays.stream(x));
Now, I want to sort the list into ascending order. I've tried this:
dataStream.sorted().collect(Collectors.toList());
but I get the compile time error
I'm confused about this, because on the examples I've seen, similar things are done without errors.
Try with
dataStream.sorted().boxed().collect(Collectors.toList());
because collect(Collectors.toList()) does not apply to a IntStream.
I also think that should be slightly better for performance call first sorted() and then boxed().
IntStream.collect() method has the following signature:
<R> R collect(Supplier<R> supplier,
ObjIntConsumer<R> accumulator,
BiConsumer<R, R> combiner);
If you really want use this you could:
.collect(IntArrayList::new, MutableIntList::add, MutableIntList::addAll);
As suggested here:
How do I convert a Java 8 IntStream to a List?
The problem is you're trying to convert an int stream to a list, but Collectors.toList only works on streams of objects, not streams of primitives.
You'll need to box the array before collecting it into the list:
dataStream.sorted().boxed().collect(Collectors.toList());

Getting return list from forEach java 8

I am trying to use a stream for something and I think I have a conceptual misunderstanding. I am trying to take an array, convert it to a stream, and .forEach item in the array I want to run a function and return a list of the results of that function from the foreach.
Essentially this:
Thing[] functionedThings = Array.stream(things).forEach(thing -> functionWithReturn(thing))
Is this possible? Am I using the wrong stream function?
What you are looking for is called the map operation:
Thing[] functionedThings = Arrays.stream(things).map(thing -> functionWithReturn(thing)).toArray(Thing[]::new);
This method is used to map an object to another object; quoting the Javadoc, which says it better:
Returns a stream consisting of the results of applying the given function to the elements of this stream.
Note that the Stream is converted back to an array using the toArray(generator) method; the generator used is a function (it is actually a method reference here) returning a new Thing array.
You need map not forEach
List<Thing> functionedThings = Array.stream(things).map(thing -> functionWithReturn(thing)).collect(Collectors.toList());
Or toArray() on the stream directly if you want an array, like Holger said in the comments.
In my case I had to use some setter of Thing, so used peek(...)
List<Thing> functionedThings = Array.stream(things)
.peek(thing -> thing.setSuccess(true))
.collect(Collectors.toList());

How do I create IntStream from Set<Integer>?

I currently do this:
Set<Integer> integers = ... // sourced from elsewhere in code
IntStream intStream = integers.stream().mapToInt(value -> value);
It seems redundant to have to map value to value, to convert the Stream<Integer> to IntStream. Is there a way to do this without that redundant mapToInt(...) section?
No, you have to use .mapToInt(value -> value) or (looks better to me) .mapToInt(Integer::intValue). As Stream is the generic class, it should work for any generic stream element type, thus it's impossible to add something simpler to the API in type-safe manner. Consider, for example, that there's asIntStream() method in Stream interface. It would certainly look better like:
IntStream intStream = integers.stream().asIntStream();
However nothing would stop you to call such method for Stream<String> for example. There's no way in Java to enable calling the method for particular generic parameter only. Thus calling it accidentally on the stream of another type would compile fine, but result in runtime exception which is bad in statically-typed language. However using .mapToInt(value -> value) or .mapToInt(Integer::intValue) you force the type checking: you must supply a function which returns int, so stringStream.mapToInt(value -> value) correctly reports compilation error.
If you really care, you are free to create a static method in your project like this:
public class Streams {
public static IntStream intStream(Collection<Integer> c) {
return c.stream().mapToInt(Integer::intValue);
}
}
And use it like
IntStream intStream = Streams.intStream(integers);

Categories