Java streams list processing order [duplicate] - java

This question already has an answer here:
Java streams lazy vs fusion vs short-circuiting
(1 answer)
Closed 4 years ago.
Can someone please explain the output of below java streams code:
List<Integer> l = new ArrayList<>();
l.add(0);
l.add(1);
l.add(2);
l.add(3);
l.add(4);
l.add(4);
l.stream()
.distinct()
.map(v -> {
System.out.println("In Map " + v);
return v;
}).forEach(v -> {
System.out.println("In ForEach " + v);
});
I expected:
In Map 0
In Map 1
In Map 2
In Map 3
In Map 4
In ForEach 0
In ForEach 1
In ForEach 2
In ForEach 3
In ForEach 4
But it prints:
In Map 0
In ForEach 0
In Map 1
In ForEach 1
In Map 2
In ForEach 2
In Map 3
In ForEach 3
In Map 4
In ForEach 4

I think it is pretty obvious :) It goes in Map and for each value does the foreach call. It's like nested loops. Get value 1 - process it, get value 2 process it ;) And that's what you get

The Stream pipeline is lazily evaluated. Meaning it performs intermediate operations on Stream elements only when such elements are required by the terminal operation - forEach in your case.
If the terminal operation required only a single element (for example findFirst()), the map() operation would be executed only for the first element of the Stream.

Related

Get all indexes of an array with a certain condition in java [duplicate]

This question already has answers here:
Find all indexes of a value in a List [duplicate]
(3 answers)
Closed 12 days ago.
I have a list of strings and I want to add to a set all indexes from array where the string is not empty,
I tried doing this:
columnNum.addAll((Collection<? extends Integer>) IntStream.range(0, row.size()).filter(i-> StringUtils.isNotEmpty(row.get(i))));
but I get an exception
You have to use boxed:
var list = List.of("","a","","b");
var set = IntStream.range(0, list.size())
.filter(i ->
!list.get(i).isEmpty()).boxed().collect(Collectors.toSet());
Collect the stream to a List first. An IntStream is not a Collection.
columnNum.addAll(IntStream.range(0, row.size())
.filter(i-> StringUtils.isNotEmpty(row.get(i)))
.boxed().collect(Collectors.toList())); // or .toList() with Java 16+

Java List Group by Model property adding values

I have a List of Teams with the following properties:
public class Team {
private String name; //get;set
private int score;//get;set
}
Is there a way to group the results of the list by Name while adding the scores?
The List can contain multiple of the same team but with different scores:
TeamA - 3
TeamB - 3
TeamA - 3
TeamC - 0
TeamB - 1
TeamD - 1
TeamE - 1
And the final list that I want to achive is something like this (ordered by score and when draw, alphabetically):
TeamA - 6
TeamB - 4
TeamD - 1
TeamE - 1
TeamC - 0
Right now, I got this with the help of frascu's answer:
teams.sort(Comparator.comparing(Team::getName)
.thenComparing(Team::getScore, Comparator.reverseOrder()));
Map<String, Integer> map = teams.stream()
.collect(groupingBy(Team::getName, summingInt(Team::getScore)));
But when I print the result from the map I get this:
TeamA - 6
TeamB - 4
TeamC - 0
TeamD - 1
TeamE - 1
The score 0 is being ordered as higher than 1
It's not necessary to sort the source list unless you don't want it to be sorted as well.
Note that by sorting the source list you can not ensure that entries of the map would be stored in sorted order because the map is expected to contain the accumulated score (i.e. multiple elements in the list could contribute to a single map entry). Hence, sorting has to be applied right in the stream after grouping the data by team-name and generating the total score of each team.
In order to maintain entries in sorted order, you need a Map implementation which is capable of maintaining the order. When mapFactory is not specified, collector would give you a general purpose implementation (for now it's HashMap) which doesn't meet this requirement.
The possible option is to use a LinkedHashMap. Note: because entries need to be ordered by both value and key, you can't achieve required ordering with a TreeMap, which maintains sorted order based on keys.
That how it might be implemented:
List<Team> teams = List.of(
new Team("TeamA", 3),
new Team("TeamB", 3),
new Team("TeamA", 3),
new Team("TeamC", 0),
new Team("TeamB", 1),
new Team("TeamD", 1),
new Team("TeamE", 1)
);
Map<String, Integer> scoreByName = teams.stream()
.collect(Collectors.toMap(
Team::getName, // keyMapper
Team::getScore, // valueMapper
Integer::sum // mergeFunction
))
.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed()
.thenComparing(Map.Entry.comparingByKey()))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(left, right) -> { throw new AssertionError("duplicates are not expected"); }, // because the source of the stream is a Map, and there couldn't be a key that occure more than once
LinkedHashMap::new // mapFactory
));
scoreByName.forEach((k, v) -> System.out.println(k + " -> " + v));
Output:
TeamA -> 6
TeamB -> 4
TeamD -> 1
TeamE -> 1
TeamC -> 0
A link to Online Demo
Using thenComparing you can apply a second sort on the elements that have the same name. In your case, you need to call reverseOrder in order to apply descending order to the score.
list.sort(Comparator.comparing(Team::getName)
.thenComparing(Team::getScore, Comparator.reverseOrder()));

How to collect two string streams into a Map in Java? [duplicate]

This question already has answers here:
How to create a map out of two arrays using streams in Java?
(3 answers)
Closed 1 year ago.
I have two Arrays as below:
k[] = {"K1","K2","K3"}
v[] = {"V1","V2","V3"}
I want to iterate these two arrays using Stream API in such a way so that I collect them as a Map as
[K1=V1,K2=V2,K3=V3]
Assuming both arrays have the same length, you could create a stream with the indexes. This can be done with IntStream.range(start,end) where the start is 0 and the end the size of your array. Because we use range, the end will not be included.
In this stream, you need to collect the result to a map, the key will be the value in first array with the given index, the value will be the value in the second array with the given index.
Do note that an IntStream is not the same as a Stream<Integer>. In this case, we will need a stream of Integers so that we can collect them in the Collector (Collector does not work with primitive types). To do this, call the method .boxed() to convert it to a Stream<Integer>
String k[] = {"K1", "K2", "K3"};
String v[] = {"V1", "V2", "V3"};
Map<String, String> result = IntStream.range(0, k.length).boxed().collect(Collectors.toMap(i -> k[i], i -> v[i]));
This gives the following result
{K1=V1, K2=V2, K3=V3}
IntStream.range(0, k.length).collect(Collectors.toMap(i -> k[i], i -> v[i]));

lambdas in removeIf

HashSet<Integer> liczby = new HashSet<Integer>();
liczby.add(1);
liczby.add(2);
liczby.add(3);
liczby.add(4);
liczby.removeIf ((Integer any) -> { return liczby.contains(3); });
for(Iterator<Integer> it = liczby.iterator(); it.hasNext();){
Integer l2 = it.next();
System.out.println(l2);
}
I can't understand why removeIf deletes not only 3 but also 1 and 2 condition should be satisfied only by 3...
Think of it this way... as long as the set contains 3 it will keep removing hence the current outcome.
If you want to remove the 3 only then do this:
liczby.removeIf(e -> e == 3);
The lambda is applied on each element and check if 3 is present, if yes it will delete the element :
1 -> 3 is present -> delete 1
2 -> 3 is present -> delete 2
3 -> 3 is present -> delete 3
4 -> 3 is not present -> don't delete 4
To remove all 3 element, you can use one of those solutions :
liczby.removeIf(any -> any.equals(3));
//-------------------------------------------------
liczby.removeIf(new Integer(3)::equals);
//-------------------------------------------------
Integer toRemove = 3;
liczby.removeIf(toRemove::equals);
TIPS
Your lambda can be simplified as :
liczby.removeIf(any -> liczby.contains(3));
For-each loop might be easier to use for simple iteration :
for(Integer i : liczby){
System.out.println(i);
}

Modify part of the list and then return the updated list [duplicate]

This question already has an answer here:
how to keep the unfiltered data in the collection in Java 8 Streaming API?
(1 answer)
Closed 7 years ago.
I am playing with Java 8 and trying to work with stream(). What I want to achieve is say, I have a list [1,2,3,4], I want to double the even numbers 2 and 4 and then return the list with the updated values. So, I will get [1,4,3,8] as a result. I tried the following but it only returned [4,8].
List<Integer> myList =
Arrays.asList(1,2,3,4);
myList = myList
.stream()
.filter(n -> n%2==0)
.map(n -> n*2)
.collect(Collectors.toList());
System.out.println(myList);
Is there a way to do what I want?
The filter method removes those elements that don't fit the condition. It does not make the rest of the operations not applicable. Move the condition inside your map method.
myList = myList
.stream()
.map(n -> (n % 2 == 0) ? n*2 : n)
.collect(Collectors.toList());

Categories