I want to collect the first n elements from a stream, without iterating through the entire thing. Is there a standard method that does this? Ala
MyList.stream()
.filter(x -> predicate(x))
.findFirstN(100)
would return a collection of up to 100 elements from the stream? My alternative is to evaluate the entire stream and then sample from the result, but that doesn't take advantage of the lazy evaluation inherent to streams.
MyList.stream()
.filter(x -> predicate(x))
.limit(100)
Related
I have a List<Stream<String>> that I get by doing a series of transactions.
The list size is dynamic (Maximum 3 elements) so I can't do:
Stream<String> finalStream = Stream.concat(list.get(0),Stream.concat(list.get(1),list.get(2));
I need to concatenate the list of Streams into one single Stream<String>.
Is there any simple way to do this?
If you have a list of lists, or a stream of streams, or any collection of collections, you can use flatMap to, well, flatten them. flatMap applies a mapping function which must return a stream to an input and streams each element of the result of the mapping function.
In your case, you could do:
var finalStream = list.stream().flatMap(x -> x);
x -> x is the identify function which returns the input unmodified. If you prefer, you can replace it with the expression Function.identity().
I am new to functional programming, and I am trying to get better.
Currently, I am experimenting with some code that takes on the following basic form:
private static int myMethod(List<Integer> input){
Map<Integer,Long> freq = input
.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
return (int) freq
.keySet()
.stream()
.filter(key-> freq.containsKey(freq.get(key)))
.count();
}
First a hashmap is used to get the frequency of each number in the list. Next, we sum up the amount of keys which have their values that also exist as keys in the map.
What I don't like is how the two streams need to exist apart from one another, where a HashMap is made from a stream only to be instantly and exclusively consumed by another stream.
Is there a way to combine this into one stream? I was thinking something like this:
private static int myMethod(List<Integer> input){
return (int) input
.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.keySet()
.stream()
.filter(key-> freq.containsKey(freq.get(key)))
.count();
}
but the problem here is there is no freq map to reference, as it is used as part of the pipeline, so the filter cannot do what it needs to do.
In summary, I don't like that this collects to a hashmap only then to convert back into a keyset. Is there a way to "streamline" (pun intended) this operation to
Not be going back and forth from stream and hashmap
Reference itself in a way without needing to declare a separate map before the pipeline.
Thank you!
Your keySet is nothing but effectively a HashSet formed of your input. So, you should make use of temporary storage such that:
Set<Integer> freq = new HashSet<>(input);
and further count, filter based on values in a single stream pipeline as
return (int) input
.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()))
.values() // just using the frequencies evaluated
.stream()
.filter(count -> freq.contains(count.intValue()))
.count();
Say I have the String "5 12 4". I'd like to turn that into an ArrayList of Integers containing 5,12 and 4 in a single functional line.
I feel there should be a way to do this by combining split(" "), turning it into a stream, using mapToInt(s->Integers.parseInt(s)) and collect(Collectors.toList()). Something like:
ArrayList<Integer> nextLine = Arrays.stream(inputLine.split(" "))
.mapToInt(s->Integer.parseInt(s))
.collect(Collectors.toList());
But that does not work, because mapToInt give me ints instead of Integers.
I know how to do it using a loop. I would like a way to do it in a single stream operation if it exists.
You can use Integer#valueOf. Note you should use Stream#map and not Steam#mapToInt though:
List<Integer> nextLine =
Arrays.stream(inputLine.split(" "))
.map(Integer::valueOf)
.collect(Collectors.toList());
mapToInt returns an IntStream and you cannot accumulate primitive elements into a ArrayList<T> therefore you can utilise the map operation which would yield a Stream<Integer> and then you can accumulate the elements into a ArrayList<T>.
That said, even if you change .mapToInt(s -> Integer.parseInt(s)) to .map(s -> Integer.parseInt(s)) your code still will not compile as the receiver type for the result is of type ArrayList<Integer> whereas the collect terminal operation will return a List<Integer> in this specific case.
Therefore to solve the remaining issue you can either have the receiver type as List<Integer> or leave the receiver type as is and then do .collect(Collectors.toCollection(ArrayList::new)); for the reduction operation yielding a specific List implementation.
Another variant to the already posted answer would be:
ArrayList<Integer> resultSet =
Pattern.compile(" ")
.splitAsStream(inputLine)
.map(Integer::valueOf)
.collect(Collectors.toCollection(ArrayList::new));
I'm new to Streams and Lambdas in Java. I have a variable like this -
List<String> lines = Arrays.asList("ab,12,bd","df,23,df","ef,98,dg");
I wanted these actions to happen.
Split each element in the list.
Extract 2nd element in the resulting array. (that is numbers).
Apply some function on it. To make it simple, let's I wanted to multiply it by 2.
Then collect the result as list, that is list containing 24,46,196.
I tried doing that in streams, but I'm not able to get. Any pointers will be helpful. Thank you.
Edit: I tried this way and got result -
List<Integer> result1 = lines.stream()
.map(l -> l.split(",")[1])
.map(l->Integer.parseInt(l))
.collect(Collectors.toList());
And got results as
[12, 23, 98]
Is this is correct way of doing stream?
It should be straightforward.
lines.stream().map(s -> Integer.parseInt(s.split(",")[1]) * 2).collect(Collectors.toList());
Update based on the updated question
Is this is correct way of doing stream?
Yes. You can further combine the two map operations into one like I have shown above.
Does the following work for you?
lines.stream().map(s -> s.split(",")[1])
.map(Integer::parseInt)
.map(f)
.collect(Collectors.toList());
where f is your function from Integer to the type you want to use in your final list.
What about:
List<Integer> result = lines.stream()
.map(line -> line.replaceFirst(".*,(\\d+),.*", "$1"))
.map(num -> 2 * Integer.parseInt(num))
.collect(Collectors.toList());
You may want to convert to int[] in that case you can use:
int[] result = lines.stream()
.map(line -> line.replaceFirst(".*,(\\d+),.*", "$1"))
.mapToInt(num -> 2 * Integer.parseInt(num))
.toArray();
I often find myself writing code like this:
return collectionOfOptionals.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.collect(toList());
But is there a way to compress those middle two lines into a single operation?
I can do this, but it feels even less satisfactory:
return collectionOfOptionals.stream()
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
.collect(toList());
It seems that this is possible in Java 9, based on this question.
return collectionOfOptionals.stream()
.flatMap(Optional::stream)
.collect(toList());
This is much nicer. But what I have already will have to do in Java 8.