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);
Related
I have two methods: funca() and funcb() which return a value of type X or a List<X> respectively like shown below:
X funca(Event e) { ... }
List<X> funcb(Event e) { ... }
I want to use them in the Stream and collect the result into a list.
These method methods should be called under different conditions, like shown below in pseudocode:
List<Event> input = // initializing the input
List<X> resultList = input.values().stream()
.(event -> event.status=="active" ? funca(event) : funcb(event))
.collect(Collectors.toList());
Can someone please tell me how I can achieve this so that whether the function returns a list of values or values?
Since one of your functions produces a Collection as a result, you need a stream operation that allows performing one-to-many transformation. For now, Stream IPA offers two possibilities: flatMap() and mapMulti().
To approach the problem, you need to take a closer look at them and think in terms of these operations.
flatMap()
This operation requires a function producing a Stream, and elements of this new stream become a replacement for the initial element.
Therefore, you need to wrap the result returned by the funca() with Singleton-Stream using Stream.of() (there's no need for wrapping the element with a List, like shown in another answer flatMap() is not capable to consume Collections).
List<X> = input.values().stream()
.flatMap(event -> "active".equals(event.getStatus()) ?
Stream.of(funca(event)) : funcb(event).stream()
)
.toList(); // for Java 16+ or collect(Collectors.toList())
mapMulti()
This operation was introduced with Java 16 and is similar to flatMap() but acts differently.
Contrary to flatMap it doesn't consume a new Stream. As an argument it expects a BiConsumer. Which in turn takes a stream element and a Consumer of the resulting type. Every element offered to the Consumer becomes a part of the resulting stream.
mapMulti() might be handy if funcb() produces a list which is very moderate in size (refer to documentation linked above for more details), otherwise flatMap() would be the right tool.
List<X> = input.values().stream()
.<X>mapMulti((event, consumer) -> {
if ("active".equals(event.getStatus())) consumer.accept(funca(event));
else funcb(event).forEach(consumer);
})
.toList(); // for Java 16+ or collect(Collectors.toList())
Sidenote: don't use == to compare reference types (like String) unless you need to make sure that both references are pointing to the same object, use equals() method instead.
Embed the result of funcA into a list and flatMap the lists:
List<X> result = input.stream()
.flatMap(e -> e.status.equals("active") ? List.of(funcA(e)) : funcB(e))
.collect(Collectors.toList());
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());
I'm try to convert a list to a map using the Collectors.toMap call. The list consists of ActivityReconcile objects. I want to pass an instance for every entry in the list into the toMap call.
The code is below and where I need the instances is denoted by ??.
final List<ActivityReconcile> activePostedList = loader.loadActivePosted(accessToken);
Map<AccountTransactionKey, ActivityReconcile> postedActiveMap =
activePostedList.stream().collect(
Collectors.toMap(
AccountTransactionKey.createNewAccountTransactionKeyFromActivityReconcileRecord(??),??));
If I understood you correctly, you will need something like
Map<AccountTransactionKey, ActivityReconcile> result = choices
.stream()
.collect(Collectors.toMap(
AccountTransactionKey::generate,
Function.identity()));
And the method (in AccountTransactionKey class) will look like
public static AccountTransactionKey generate(ActivityReconcile reconcile) {...}
I've replaced createNewAccountTransactionKeyFromActivityReconcileRec by generate for making the answer more readable and understandable.
To "fix" your code with the least changes, add a lambda parameter:
activePostedList.stream().collect(Collectors.toMap(
ar -> AccountTransactionKey.createNewAccountTransactionKeyFromActivityReconcileRecord(ar)),
o -> o));
or use a method reference:
activePostedList.stream().collect(Collectors.toMap(
AccountTransactionKey::createNewAccountTransactionKeyFromActivityReconcileRecord, o -> o));
btw, I can't recall ever seeing a method name as long as createNewAccountTransactionKeyFromActivityReconcileRecord - for readability, consider reducing it to just create(), since the return type and parameter type are enough to distinguish it from other factory methods you may have.
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());
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.