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);
Related
I am searching for an elegant way to filter a list for only the elements that are unique. An example:
[1, 2, 2, 3, 1, 4]
-> [3, 4] // 1 and 2 occur more than once
Most solutions I found manually compute the occurrences of all elements and then filter by the elements that have exactly one occurrence.
That does not sound too elegant to me, maybe there is a better solution, a best practice or a name for a data-structure that solves this already? I was also thinking about maybe utilizing streams, but I do not know how.
Note that I am not asking for duplicate removal, i.e. [1, 2, 3, 4] but for keeping only the unique elements, so [3, 4].
The order of the resulting list or what type of Collection exactly does not matter to me.
I doubt there is a better approach than actually counting and filtering for the ones that only appeared once. At least, all approaches I can think of will use something similar to that under the hood.
Also, it is not clear what you mean by elegant, readability or performance? So I will just dump some approaches.
Stream counting
Here is a stream-variant that computes number of occurrences (Map) and then filters for elements that appear only once. It is essentially the same as what you described already, or what Bags do under the hood:
List<E> result = elements.stream() // Stream<E>
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting())) // Map<E, Long>
.entries() // Set<Entry<E, Long>>
.stream() // Stream<Entry<E, Long>>
.filter(entry -> entry.getValue() == 1)
.map(Entry::getKey)
.collect(Collectors.toList());
It requires two full iterations over the data-set. Since it uses the Stream-API, the operations support multi-threading right from the get-go though. So if you have lots of elements, this might be pretty fast due to that.
Manual Set
Here is another approach that reduces iteration and lookup time by manually collecting into a Set to identify duplicates as fast as possible:
Set<E> result = new HashSet<>();
Set<E> appeared = new HashSet<>();
for (E element : elements) {
if (result.contains(element)) { // 2nd occurrence
result.remove(element);
appeared.add(element);
continue;
}
if (appeared.contains(element)) { // >2nd occurrence
continue;
}
result.add(element); // 1st occurrence
}
As you see, this only requires one iteration over the elements instead of multiple.
This approach is elegant in a sense that it does not compute unnecessary information. For what you want, it is completely irrelevant to compute how often exactly elements appear. We only care for "does it appear once or more often?" and not if it appears 5 times or 11 times.
You can use Bag to count occurrences (getCount(1) for unique)
Bag is a collection that allows storing multiple items along with their repetition count:
public void whenAdded_thenCountIsKept() {
Bag<Integer> bag = new HashBag<>(
Arrays.asList(1, 2, 3, 3, 3, 1, 4));
assertThat(2, equalTo(bag.getCount(1)));
}
Or CollectionBag
Apache Collections' library provides a decorator called the CollectionBag. We can use this to make our bag collections compliant with the Java Collection contract:
And get unique set:
bag.uniqueSet();
Returns a Set of unique elements in the Bag.
One need first to collect all, reached the end for deleting groups of more than 1 element.
Map<String, Long> map = Stream.of("a", "b", "a", "a", "c", "d", "c")
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
map.entrySet()
.stream()
.filter(e -> e.getValue() == 1L)
.map(e -> e.getKey())
.forEach(System.out::println);
Or in one go:
Stream.of("a", "b", "a", "a", "c", "d", "c")
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.entrySet()
.stream()
.filter(e -> e.getValue() == 1L)
.map(e -> e.getKey())
.forEach(System.out::println);
The idea of using a map to accumulate frequency counts sounds like a good one: it runs in roughly linear (O(n)) time and only requires O(n) extra space.
Here's an algorithm that requires zero extra space, at the expense of running in O(n^2) time:
public static <T> void retainSingletons(List<T> list)
{
int i = 0;
while (i < list.size()) {
boolean foundDup = false;
int j = i + 1;
while (j < list.size()) {
if (list.get(i).equals(list.get(j))) {
list.remove(j);
foundDup = true;
} else {
++j;
}
}
if (foundDup) {
list.remove(i);
} else {
++i;
}
}
}
The idea is straightforward: step a slow pointer, i, over the list until it runs off the end; for each value of i, run a fast pointer j from i+1 until the end of the list, removing any list[j] that's a duplicate of list[i]; after j runs out, if any duplicates of list[i] were found and removed, also remove list[i].
The following will work using Eclipse Collections:
IntList list = IntLists.mutable.with(1, 2, 2, 3, 1, 4);
IntSet unique = list.toBag().selectUnique();
System.out.println(unique);
Using an IntList removes the need to box the int values and Integer objects.
Note: I am a committer for Eclipse Collections.
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.
In class we used this code to generate a Fibonacci sequence via an Integer Stream.
Can someone please explain to me what the .map() function is doing in this code?
public class fibStream {
public static Stream<Integer> getFibStream() {
return Stream.iterate(new Integer[] {0,1}, s -> new Integer[]{s[1], s[0] + s[1]})
.map(s -> s[0]); //what is .map() doing here?
}
}
Owing to the seed new Integer[] {0,1}, you would be iterating on Integer[] with two elements. Iterating with a new Integer[] every step with its values re-evaluated.
The map is just accessing the first of those Integer to finally consume(make use of) it.
For an example, better composed implementation of the same could look like(print 20 first fibonacci element) :
Stream.iterate(new Integer[] {0,1}, s -> new Integer[]{s[1], s[0] + s[1]})
.limit(20) // short-circuit
.map(s -> s[0])
.forEach(System.out::println); // consume the mapped element
From the comments by JollyJoker
Additional clarification; the fibonacci stream is made of arrays like
{0,1} {1,1} {1,2} {2,3} {3,5} and .map(s -> s[0]) gets each array,
returning the first number from them, like 0 1 1 2 3.
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();