I want to convert a List<String> to an IntStream. Suppose that my list is like ["abc", "de", "fghi"]. Then the IntStream that I want is like 1,1,1,2,2,3,3,3,3. (The number of occurrences of a number i in the IntStream depends on the length of ith string in the given list)
I wrote the following method for that (it won't compile):
private static IntStream getSingleIntStream(List<String> list) {
final AtomicInteger atomicInteger = new AtomicInteger();
return list.stream()
.map(str -> {
atomicInteger.incrementAndGet();
return IntStream.range(0, str.length())
.map(i -> atomicInteger.get());
}); // Now I don't understand how can I convert this Stream<IntStream> to a single IntStream
}
But I don't understand how can I convert can I convert a Stream<IntStream> to a single IntStream. (My guess is that we can use flatMap somehow, but I don't exactly get how to use it.)
I'm using IntStream instead of Stream<Integer> to avoid auto-boxing and make my whole system more efficient.
The other solution would be like this:
IntStream result = IntStream.range(0,list.size())
.flatMap(i-> IntStream.range(0, list.get(i)
.length())
.map(j->i+1));
Expanding over the comment of #JB_Nizet
The method that I need to use is flatMapToInt.
private static IntStream getSingleIntStream(List<String> list) {
final AtomicInteger atomicInteger = new AtomicInteger();
return list.stream()
.flatMapToInt(str -> {
atomicInteger.incrementAndGet();
return IntStream.range(0, str.length())
.map(i -> atomicInteger.get());
});
}
The solution would be to use flatMapToInt:
private static IntStream getSingleIntStream(List<String> list) {
final AtomicInteger atomicInteger = new AtomicInteger();
return list.stream()
.flatMapToInt(str -> {
atomicInteger.incrementAndGet();
return IntStream.range(0, str.length())
.map(i -> atomicInteger.get());
});
}
but I'd rethink what you'd like to achieve here. Now, each String in the initial list will be replaced with a number taken from AtomicInteger (and this will be repeated String.length() times for each String):
getSingleIntStream(Arrays.asList("a", "bc")).forEach(System.out::println); // 1 2 2
I assume you wanted to number each char from every String:
private static IntStream getSingleIntStream(List<String> list) {
AtomicInteger atomicInteger = new AtomicInteger();
return list.stream()
.flatMapToInt(str -> IntStream.range(0, str.length())
.map(i -> atomicInteger.incrementAndGet()));
}
// 1 2 3
You are absolutely correct about flatMap, as Hadi J already shows in a good answer. I just wanted to offer my variant of the same:
private static IntStream getSingleIntStream(List<String> list) {
return IntStream.rangeClosed(1, list.size())
.flatMap(i -> list.get(i - 1).chars().map(ch -> i));
}
You may find this version more natural or concise. In any case both versions have the advantage of avoiding the AtomicInteger and the side effect of the stream pipeline on it. A stream pipeline should be free from side effects.
Let’s also see it in action:
List<String> list = List.of("abc", "de", "fghi");
int[] nums = getSingleIntStream(list).toArray();
System.out.println(Arrays.toString(nums));
[1, 1, 1, 2, 2, 3, 3, 3, 3]
When you need the index of an element inside a stream, the general trick is to start out from an IntStream of the indices and inside your stream pipeline make the lookup of the elements from the indices (list.get(i - 1)). In this case I am (unconventionally) using 1-based indices because you wanted your resulting numbers to start from 1. So we need to subtract 1 in the list lookup.
Related
public static int construction(String myString) {
Set<Character> set = new HashSet<>();
int count = myString.chars() // returns IntStream
.mapToObj(c -> (char)c) // Stream<Character> why is this required?
.mapToInt(c -> (set.add(c) == true ? 1 : 0)) // IntStream
.sum();
return count;
}
The above code will not compile without:
.mapObj(c -> (char)c)
// <Character> Stream<Character> java.util.stream.IntStream.mapToObj(IntFunction<? extends Character> mapper)
If i remove it, I get the following error
The method mapToInt((<no type> c) -> {}) is undefined for the type IntStream
Can someone explain this? It seems like I am starting with and IntStream, converting to a Stream of Characters and then back to IntStream.
The method CharSequence::chars returns the IntStream, which of course doesn't provide any method converting to int, such as mapToInt, but mapToObj instead. Therefore the method IntStream::map(IntUnaryOperator mapper) which both takes returns int as well shall be used since IntUnaryOperator does the same like Function<Integer, Integer> or UnaryOperator<Integer>:
int count = myString.chars() // IntStream
.map(c -> (set.add((char) c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(c -> set.add((char) c)) // IntStream
.count();
Also, using Set<Integer> helps you to avoid conversion to a Character:
Set<Integer> set = new HashSet<>();
int count = myString.chars() // IntStream
.map(c -> (set.add(c) ? 1 : 0)) // IntStream
.sum();
long count = myString.chars() // IntStream
.filter(set::add) // IntStream
.count();
However, regardless of what you try to achieve, your code is wrong by principle. See the Stateless behaviors. Consider using the following snippet which lambda expressions' results are not dependent on the result of a non-deterministic operation, such as Set::add.
Stream pipeline results may be nondeterministic or incorrect if the behavioral parameters to the stream operations are stateful.
long count = myString.chars() // IntStream
.distinct() // IntStream
.count();
You can also collect to a set and then take the size without using an explicit map.
It does not require using external state to contain the characters.
long count = str.chars().boxed().collect(Collectors.toSet()).size();
But imho, the more direct approach which was already mentioned is cleaner in appearance and the one I would prefer to use.
long count = str.chars().distinct().count();
Because String.chars() is already returning an IntStream and IntStream does not have mapToInt function
You could use a filter instead then count:
int count = myString.chars()
.filter(c -> set.add(c) == true)
.count();
I admit that I made this so slubby last midnight!
As mentioned by the comments, here is the required fixes.
Thank you for mentioning.
long count = myString.chars()
.filter(c -> set.add((char)c))
.count();
Result of both approach are same which is best practise to use. I am new bee to java 8. i am little bit confused on stream.map and stream.filter
List<String> alpha =
Arrays.asList("a", "b", "csddddddddddd",
"d", "ssdddddddddd", "sw", "we", "wew");
// Java 8
List<String> collect = alpha.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
List<Integer> collect2 = alpha.stream()
.map(s -> s.length())
.collect(Collectors.toList());
List<Integer> collect3 = collect2.stream()
.filter(s -> s > 10)
.collect(Collectors.toList());
List<Integer> collect4 = collect2.stream()
.map(s -> {
Integer temp = 0;
if (s > 10) {
temp = s;
}
return temp;
})
.filter(s -> s > 10)
.collect(Collectors.toList());
Result of both List are same :
[13, 12]
[13, 12]
what is best approach. and what is best approach in this regards.
If you want to filter the elements of a Stream by some condition (i.e. remove elements that don't satisfy the condition), use filter, not map.
The purpose of map is to convert an element of one type to an element of another type.
The only reason you get the same results in collect3 and collect4 is that after applying map in collect4, you apply a filter, which removes all the 0s produced by map.
The entire .map(s ->{ Integer temp = 0;if(s>10) {temp=s;} return temp;}) call is redundant. That's a very unreadable and inefficient way to write code.
I've just started playing with Java 8 lambdas and I'm trying to implement some of the things that I'm used to in functional languages.
For example, most functional languages have some kind of find function that operates on sequences, or lists that returns the first element, for which the predicate is true. The only way I can see to achieve this in Java 8 is:
lst.stream()
.filter(x -> x > 5)
.findFirst()
However this seems inefficient to me, as the filter will scan the whole list, at least to my understanding (which could be wrong). Is there a better way?
No, filter does not scan the whole stream. It's an intermediate operation, which returns a lazy stream (actually all intermediate operations return a lazy stream). To convince you, you can simply do the following test:
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
int a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.get();
System.out.println(a);
Which outputs:
will filter 1
will filter 10
10
You see that only the two first elements of the stream are actually processed.
So you can go with your approach which is perfectly fine.
However this seems inefficient to me, as the filter will scan the whole list
No it won't - it will "break" as soon as the first element satisfying the predicate is found. You can read more about laziness in the stream package javadoc, in particular (emphasis mine):
Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
return dataSource.getParkingLots()
.stream()
.filter(parkingLot -> Objects.equals(parkingLot.getId(), id))
.findFirst()
.orElse(null);
I had to filter out only one object from a list of objects. So i used this, hope it helps.
In addition to Alexis C's answer, If you are working with an array list, in which you are not sure whether the element you are searching for exists, use this.
Integer a = list.stream()
.peek(num -> System.out.println("will filter " + num))
.filter(x -> x > 5)
.findFirst()
.orElse(null);
Then you could simply check whether a is null.
Already answered by #AjaxLeung, but in comments and hard to find.
For check only
lst.stream()
.filter(x -> x > 5)
.findFirst()
.isPresent()
is simplified to
lst.stream()
.anyMatch(x -> x > 5)
import org.junit.Test;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
// Stream is ~30 times slower for same operation...
public class StreamPerfTest {
int iterations = 100;
List<Integer> list = Arrays.asList(1, 10, 3, 7, 5);
// 55 ms
#Test
public void stream() {
for (int i = 0; i < iterations; i++) {
Optional<Integer> result = list.stream()
.filter(x -> x > 5)
.findFirst();
System.out.println(result.orElse(null));
}
}
// 2 ms
#Test
public void loop() {
for (int i = 0; i < iterations; i++) {
Integer result = null;
for (Integer walk : list) {
if (walk > 5) {
result = walk;
break;
}
}
System.out.println(result);
}
}
}
A generic utility function with looping seems a lot cleaner to me:
static public <T> T find(List<T> elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
static public <T> T find(T[] elements, Predicate<T> p) {
for (T item : elements) if (p.test(item)) return item;
return null;
}
In use:
List<Integer> intList = Arrays.asList(1, 2, 3, 4, 5);
Integer[] intArr = new Integer[]{1, 2, 3, 4, 5};
System.out.println(find(intList, i -> i % 2 == 0)); // 2
System.out.println(find(intArr, i -> i % 2 != 0)); // 1
System.out.println(find(intList, i -> i > 5)); // null
Improved One-Liner answer: If you are looking for a boolean return value, we can do it better by adding isPresent:
return dataSource.getParkingLots().stream().filter(parkingLot -> Objects.equals(parkingLot.getId(), id)).findFirst().isPresent();
I am trying to generate pairs of integers - I have a class Pair with a constructor taking 2 ints. The following code works but seems rather clunky - in particular the conversion from an intStream to an object stream using mapToObj(Integer::new).
private static List<Pair> success() {
return IntStream.range(0, 10).
mapToObj(Integer::new).flatMap(i -> IntStream.range(12, 15).
mapToObj(j -> new Pair(i, j))).
collect(Collectors.toList());
}
Firstly does anyone have a more elegant way to do this ?
Secondly when I refactored to extract some streams as variables, I get an error: IllegalStateException: stream has already been operated upon or closed. Here is the refactored method - does anyone know if this a problem with the code ?
static List<Pair> fail() {
Stream<Integer> outer = IntStream.range(0, 10).mapToObj(Integer::new);
IntStream inner = IntStream.range(12, 15);
Stream<Pair> pairStream = outer.flatMap(i ->
inner.mapToObj(j -> new Pair(i, j)));
return pairStream.collect(Collectors.toList());
}
It is possible to make it a bit more concise by replacing mapToObj(Integer::new) with boxed- but apart from that, Java is not that concise:
IntStream.range(0, 10)
.boxed()
.flatMap(i -> IntStream.range(12, 15)
.mapToObj(j -> new Pair(i, j)))
.collect(Collectors.toList());
As for the second question: There are other answers which link to the problem. The concrete problem is that inner is not used once, but each time of the outer flatMap().
This way it works:
final IntStream range = IntStream.range(0, 10);
List<Pair> ps = range
.boxed().flatMap(i -> {
final IntStream range1 = IntStream.range(12, 15);
return range1.
mapToObj(j -> new Pair<>(i, j));
}).
collect(Collectors.toList());
Why not use plain for-loops? Plain for-loops will:
Look nicer
Make your intent clear
static List<Pair> fail() {
List<Pair> pairs = new ArrayList<>(30);
for (int i = 0; i < 10; i++) {
for (int j = 12; j < 15; j++) {
pairs.add(new Pair(i, j));
}
}
return pairs;
}
If your Pair class accepts primitive ints you can eliminate the unnecessary boxing this way:
private static List<Pair> success() {
return IntStream.range(0, 10).
mapToObj(i -> IntStream.range(12, 15).
mapToObj(j -> new Pair(i, j))).
flatMap(Function.identity()).
collect(Collectors.toList());
}
As for extracting streams into variables, you may create a supplier instead:
private static List<Pair> success() {
Supplier<IntStream> inner = () -> IntStream.range(12, 15);
return IntStream.range(0, 10).
mapToObj(i -> inner.get().
mapToObj(j -> new Pair(i, j))).
flatMap(Function.identity()).
collect(Collectors.toList());
}
Though it seems unnecessary for me to extract the stream into the variable.
I have a 2D array that I want to print using IntStream.
this is the array,
int[][] twoD = { { 1, 2 }, { 3, 4 }, { 5, 6 } };
Now, using nested loop this can be done like,
for (int i = 0; i < twoD.length; i++) {
for (int j = 0; j < twoD[i].length; j++) {
System.out.println(twoD[i][j]);
}
}
but i want to use IntStream. and I recently learned about its flatMap method which I can use to achieve that, so I tried this,
IntStream.range(0, twoD.length)
.flatMap(j -> IntStream.range(0, twoD[j].length))
.forEach(System.out::print);
and it outputs 010101.
One reason for the output being 010101 is that 010101 are index values not the values in the array and I have to map these values to the array values using something like, i -> twoD[i]
so I tried this,
IntStream.range(0, twoD.length)
.map(i -> twoD[i])
.flatMap(j -> IntStream.range(0, twoD[j].length))
.forEach(System.out::print);
but it gives error on map(i -> twoD[i]),
Type mismatch: cannot convert from int[] to int
but if it was 1D array then it would have worked, for example,
int[] oneD = { 1, 2, 3, 4, 5, 6 };
IntStream.range(0, oneD.length)
.map(i -> oneD[i])
.forEach(System.out::print);
How can I print the 2D array using the above approach ?
I think you overcomplicate things. You could just do it like this:
Stream.of(twoD).flatMapToInt(IntStream::of).forEach(System.out::println);
What it does is:
get a Stream<int[]> from the int[][] array
flatMap each int[] to an IntStream so that you get back an IntStream with all the elements of the 2D array
for each element, print it
What you want to do is achievable but not very readable. A formal translation of your nested loop would be:
IntStream.range(0, twoD.length)
.forEach(i -> IntStream.range(0, twoD[i].length)
.forEach(j -> System.out.println(twoD[i][j])));
which produces the same output, but as you can see it's not very readable. Here you don't need to stream the indices so the first approach with flatMapToInt is the best.
Now why your solution doesn't compile?
It's because map on an IntStream expect a mapping function that gives you back an int but you give an int[]. You need to use mapToObj and then again flatMapToInt to get an IntStream and finally print the contents (this isn't the only solution though).
IntStream.range(0, twoD.length)
.mapToObj(i -> twoD[i])
.flatMapToInt(IntStream::of)
.forEach(System.out::print);
Do you gain in readability? Not really, so I suggest to use the first approach which is clear and concise.
Note that the last solution could be also written as:
IntStream.range(0, twoD.length)
.flatMap(i -> IntStream.of(twoD[i]))
.forEach(System.out::print);
... but I still prefer the first approach ! :)
Why not stream the array itself:
Arrays.stream(twoD)
.flatMapToInt(Arrays::stream)
.forEach(System.out::println);