LinkedList<Double> list = new LinkedList<Double>();
list.add(9.5);
list.add(4.9);
list.add(3.2);
list.add(4.9);
I want to count the duplicate element in the list through a stream and put them into a HashMap which represent the occurrence of each number in the list:
e.g: (9.5=1, 4.9=2, 3.2=1)
Does anybody know how this works?
Using Collections.frequency
Make a list of all the distinct values, and for each of them, count their occurrences using the Collections.frequency method. Then collect into a Map
Map<Double, Integer> result = list.stream()
.distinct()
.collect(Collectors.toMap(
Function.identity(),
v -> Collections.frequency(list, v))
);
Using Collectors.groupingBy
I think it is not as nice as the example above.
Map<Double, Integer> result2 = list.stream()
.collect(Collectors.groupingBy(Function.identity())) // this makes {3.2=[3.2], 9.5=[9.5], 4.9=[4.9, 4.9]}
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().size())
);
Plain for loop
A plain for loop is quite short, you might not need streams and lambdas
Map<Double, Integer> map = new HashMap<>();
for(Double d : list)
map.put(d, map.containsKey(d) ? map.get(d)+1 : 1);
Using forEach
Even shorter with forEach
Map<Double, Integer> map = new HashMap<>();
list.forEach(d -> map.put(d, map.containsKey(d) ? map.get(d)+1 : 1));
Another way, using Collectors.counting which doesn't need the distinct.
Map<Double, Long> frequencies = list.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
Related
I have the following maps:
{21=0, 22=2, 11=0, 12=0}
{21=3, 22=0, 11=6, 12=3}
{21=6, 22=0, 11=7, 12=0}
{21=5, 22=7, 11=9, 12=1}
The following code returns these maps:
for (Chrom t: obj.getChroms) {
Map<Integer, Integer> result = t.getExecutionCount();
}
The method getExecutionCount() returns a single map. For the example I have given above, I have four chroms where each chrom will returns a single map.
I would like to sum the values of each key seperately so that the final result will be:
21 = 14
22 = 9
11 = 22
12 = 4
Is it possible to use stream to do that? If not, how can I do that?
Try this:
List<Map<Integer, Integer>> maps;
Map<Integer, Integer> result = maps.stream()
.map(Map::entrySet)
.flatMap(Collection::stream)
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.summingInt(Map.Entry::getValue)));
You can create Stream of maps and the use flatMap,
Stream.of(map1, map2, map3)
.flatMap(m -> m.entrySet()
.stream())
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.summingInt(Map.Entry::getValue)
)
);
It's possible using Stream. It should work (I can't compile right now unfortunately)
Map<Integer, Integer> result = Stream.of(map1, map2, map3, map4)
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::sum)
);
A little explanation
Stream over all your Maps
Make a unique Stream of all entries contained inside of your Maps
Group every by key
For the collisions, use Integer::sum which will reduce both values for the same key
I am using Java8 to achieve the below things,
Map<String, String> m0 = new HashMap<>();
m0.put("x", "123");
m0.put("y", "456");
m0.put("z", "789");
Map<String, String> m1 = new HashMap<>();
m1.put("x", "000");
m1.put("y", "111");
m1.put("z", "222");
List<Map<String, String>> l = new ArrayList<>(Arrays.asList(m0, m1));
List<String> desiredKeys = Lists.newArrayList("x");
List<Map<String, String>> transformed = l.stream().map(map -> map.entrySet().stream()
.filter(e -> desiredKeys.stream().anyMatch(k -> k.equals(e.getKey())))
.collect(Collectors.toMap(e -> e.getKey(), p -> p.getValue())))
.filter(m -> !m.isEmpty())
.collect(Collectors.toList());
System.err.println(l);
System.err.println(transformed);
List<String> values = new ArrayList<>();
for (Map<String,String> map : transformed) {
values.add(map.values().toString());
System.out.println("Values inside map::"+map.values());
}
System.out.println("values::"+values); //values::[[123], [000]]
Here, I would like to fetch only the x-values from the list. I have achieved it but it is not in a proper format.
Expected output:
values::[123, 000]
Actual output:
values::[[123], [000]]
I know how to fix the actual output. But is there any easy way to achieve this issue? Any help would be appreciable.
You do not need to iterate over the entire map to find an entry by its key. That's what Map.get is for. To flatten the list of list of values, use flatMap:
import static java.util.stream.Collectors.toList;
.....
List<String> values = l.stream()
.flatMap(x -> desiredKeys.stream()
.filter(x::containsKey)
.map(x::get)
).collect(toList());
On a side note, avoid using l (lower case L) as a variable name. It looks too much like the number 1.
I’m not sure Streams will help, here. It’s easier to just loop through the Maps:
Collection<String> values = new ArrayList<>();
for (Map<String, String> map : l) {
Map<String, String> copy = new HashMap<>(map);
copy.keySet().retainAll(desiredKeys);
values.addAll(copy.values());
}
Flat map over the stream of maps to get a single stream representing the map entries of all your input maps. From there, you can filter out each entry whose key is not contained in the desired keys. Finally, extract the equivalent value of each entry to collect them into a list.
final List<String> desiredValues = l.stream()
.map(Map::entrySet)
.flatMap(Collection::stream)
.filter(entry -> desiredKeys.contains(entry.getKey()))
.map(Map.Entry::getValue)
.collect(Collectors.toList());
EDIT
This assumes that if a map has the key "x" it must also has the key "y" so to fetch the corredponding value.
final List<String> desiredValues = l.stream()
.filter(map -> map.containsKey("x"))
.map(map -> map.get("y"))
.collect(Collectors.toList());
I have a list of items as below
List<SomeModel> smList = new ArrayList<>();
smList.add(new SomeModel(1L,6.0f));//id = 1L and capacity = 6.0f
smList.add(new SomeModel(2L,7.0f));
smList.add(new SomeModel(3L,7.0f));
smList.add(new SomeModel(4L,7.0f));
Now I am converting this list into
Map<Float, Set<Long>> complexList = new HashMap<>();
complexList = smList.stream().collect(Collectors.groupingBy(SomeModel::getCapacity,
Collectors.mapping(SomeModel::getId, Collectors.toSet())));
here complexList
gives output as
7.0=[2, 3, 4]
6.0=[1]
Now I need to count number of values for each "capacity" giving output as
7.0=3
6.0=1
I tried
Map<Float, Long> complexCount = complexList.entrySet().stream().
collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, Collectors.counting())));
complexCount.forEach((k,v)->System.out.println(k+"="+v));
and it outputs
6.0=1
7.0=1
I must be mistaking in understanding streams or not be using right methods. Can anyone suggest an approach or a solution? Reference link to streams will also be helpful.
if all you want to do is print each key of the map along with the size of the corresponding value, then there is no need to stream again as it causes unnecessary overhead. simply iterate overly the complexList and print it like so:
complexList.forEach((k,v)->System.out.println(k+"="+v.size()));
or if you really want a map then one could also do:
Map<Float, Integer> accumulator = new HashMap<>();
complexList.forEach((k,v)->accumulator.put(k, v.size()));
You are making it very complex. Easier solution below:
Map<Float, Long> complexCount = complexList
.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> new Long(entry.getValue().size())
)
);
Here, you just need to call Collectors.toMap. It has two functions one for key and another for value of the map.
If there is no restriction of using Long as Map value type, then :
Map<Float, Integer> complexCount = complexList
.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().size()
)
);
You can make use of multiple Collectors and collectingAndThen(). And don't even need to collect it to an intermediate map:
import static java.util.stream.Collectors.*;
/* ... */
Map<Float, Integer> collect = smList.stream()
.collect(groupingBy(SomeModel::getCapacity,
collectingAndThen(
mapping(SomeModel::getId, toSet()),
Set::size
)
));
I have a List<Map.Entry<Double, Boolean>> feature.
I would like to count the number of occurrences of the possible values of Boolean in the list.
The current attempt I have made is
Map<Boolean, List<Map.Entry<Double, Boolean>>> classes =
feature.stream().collect(Collectors.groupingBy(Map.Entry::getValue));
Instead of the Map<Boolean, List<Map.Entity<Double, Boolean> I would like a Map<Boolean, Integer> where the integer is the number of occurances.
I have tried
Map<Boolean, List<Map.Entry<Double, Boolean>>> classes =
feature.stream().collect(Collectors.groupingBy(Map.Entry::getValue, List::size));
But this throws a no suitable method function.
I am new to the stream API so any help achieving this would be greatly appreciated!
The other answers work perfectly, but if you insist on getting a Map<Boolean,Integer> you need this:
Map<Boolean,Integer> result = feature.stream()
.map(Map.Entry::getValue)
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.collectingAndThen(Collectors.counting(), Long::intValue)));
You can use map function to get list of Booleans and groupingBy it:
Map<Boolean, Long> collect = feature.stream()
.map(Map.Entry::getValue)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
This will give you a result of Map<Boolean, Long>:
List<Map.Entry<Double, Boolean>> feature = new ArrayList<>();
Map<Boolean, Long> result = feature
.stream()
.map(Map.Entry::getValue)
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
As for example, there are two lists:
List<Double> list1 = Arrays.asList(1.0, 2.0);
List<String> list2 = Arrays.asList("one_point_zero", "two_point_zero");
Using Stream, I want to create a map composed of these lists, where list1 is for keys and list2 is for values. To do it, I need to create an auxiliary list:
List<Integer> list0 = Arrays.asList(0, 1);
Here is the map:
Map<Double, String> map2 = list0.stream()
.collect(Collectors.toMap(list1::get, list2::get));
list0 is used in order list1::get and list2::get to work. Is there a simpler way without creation of list0? I tried the following code, but it didn't work:
Map<Double, String> map2 = IntStream
.iterate(0, e -> e + 1)
.limit(list1.size())
.collect(Collectors.toMap(list1::get, list2::get));
Instead of using an auxiliary list to hold the indices, you can have them generated by an IntStream.
Map<Double, String> map = IntStream.range(0, list1.size())
.boxed()
.collect(Collectors.toMap(i -> list1.get(i), i -> list2.get(i)));
Indeed the best approach is to use IntStream.range(startInclusive, endExclusive) in order to access to each element of both lists with get(index) and finally use Math.min(a, b) to avoid getting IndexOutOfBoundsException if the lists are not of the exact same size, so the final code would be:
Map<Double, String> map2 = IntStream.range(0, Math.min(list1.size(), list2.size()))
.boxed()
.collect(Collectors.toMap(list1::get, list2::get));
This works for me but is O(n^2):
Map<Double, String> collect =
list1.stream()
.collect(
toMap(Double::doubleValue,
item -> list2.get(list1.indexOf(item))));