Keeping order on hash map keys during collecting them [duplicate] - java

This question already has answers here:
How do I keep the iteration order of a List when using Collections.toMap() on a stream?
(6 answers)
Closed 5 years ago.
I have the following hash map
Map<String,Double> map_1 = new LinkedHashMap<>();
with some keys: e.g. ["hello_1", "hello_2", "hello_3"].
Then, I iterate through these keys using stream API and saving new results in map2:
Map<String,Double> map_2 = new LinkedHashMap<>();
map_2 = map_1.keySet()
.stream()
.collect(
Collectors.toMap(entry -> entry,
entry -> {
Double value = map_1.get(entry);
return (value + 5);
}));
but the new hash map has keys in another order, despite it is defined as LinkedHashMap. I think the problem is during stream + collect steps.
Anyone could suggest me a solution?
Thanks

The order of the keys actually gets messed up in the collect step in this case.
You need to specify that you want to collect to a LinkedHashMap.

Related

Sorting a Map<String, Object> by Object Parameter in Java [duplicate]

This question already has answers here:
TreeMap sort by value
(13 answers)
Closed 2 years ago.
I have the following problem:
I have a Map consisting of String as a key and Object as a value. I want to sort the Map's keys by one of the Object's parameters.
Map<String, Trainer> trainers = new TreeMap<>(); <-- This is my map
The object Trainer has parameters - name and points.
I want to sort the Map by who has the most points.
How do I do that with the help of Stream API?
You can use that code, for example:
Map<String, Trainer> source = new HashMap<>();
source.put("Z", new Trainer("z", 2));
source.put("B", new Trainer("b", 6));
source.put("C", new Trainer("c", 1));
source.put("D", new Trainer("d", 11));
LinkedHashMap<String, Trainer> result = source.entrySet().stream()
.sorted(Map.Entry.comparingByValue(Comparator.comparing(Trainer::getPoint)))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue, (s1, s2) -> s1, LinkedHashMap::new));
Output:
{C=Trainer{name='c', point=1}, Z=Trainer{name='z', point=2}, B=Trainer{name='b', point=6}, D=Trainer{name='d', point=11}}
You have ordered with LinkedHashMap.
When you need TreeMap use new TreeMap<>(result);
If you want to compare your Trainer's object by name change the comparator to:
Comparator.comparing(Trainer::getName)
Also you can have more hard comparing by several fields, for example:
Comparator.comparing(Trainer::getPoint).thenComparing(Trainer::getName)

Merge list of maps with duplicate keys [duplicate]

This question already has answers here:
Merge map of arrays with duplicate keys
(8 answers)
Merging a list of maps into a single map
(5 answers)
Closed 2 years ago.
I have a list of HashMap<Integer, ArrayList<String>> and would like to merge them in a loop.
The problem is that each map's key starts from 0 so the keys will be duplicated. The putAll() does not work as it overrides the keys and always gives me the last map.
I have seen examples of merging two maps by using Stream but in my case, there might be more than 2 maps. I am trying to generate a merged map that has an incremental key. For example:
Let's say I have 2 maps in the list (could be more) and both keys start from 0 but ends at different values.
1st map, the key starts at 0 ends at 10
2nd map, the key starts at 0 ends at 15
Is it possible to add the second map with the key starting at 11?
In the end, I need a merged map in which the first key starts at 0 and the last key ends at 25.
Assuming you have a list of maps, where the keys of each map are integers in range [0-k], [0-n], [0, r] ... and your resulting map should a key set in the range of [0 - (k+n+r..)] something like below should work:
public static void main(String[] args) throws IOException {
//example list of maps
List<Map<Integer,List<String>>> mapList = List.of(
Map.of( 0,List.of("foo","foo"),
1,List.of("bar","bar"),
2,List.of("baz","baz")),
Map.of( 0,List.of("doo","doo"),
1,List.of("gee","gee"),
2,List.of("woo","woo")),
Map.of( 0,List.of("cab","cab"),
1,List.of("kii","kii"),
2,List.of("taa","taa"))
);
AtomicInteger ai = new AtomicInteger();
Map<Integer,List<String>> result =
mapList.stream()
.flatMap(map -> map.values().stream())
.collect(Collectors.toMap(list -> ai.getAndIncrement(), Function.identity()));
result.forEach((k,v) ->{
System.out.println(k + " : " + v);
});
}
I'd iterate over any number of maps you have, and then for each map you want to combine, iterate over the entries. For each entry you can use computeIfAbsent to conditionally create an empty list for the key, and then call addAll on the value. E.g.:
List<Map<Integer, List<String>>> maps = List.of(
Map.of(1, List.of("hello")),
Map.of(2, List.of("world")),
Map.of(1, List.of("a"), 2, List.of("b"))
);
Map<Integer, List<String>> combined = new HashMap<>();
for (Map<Integer, List<String>> map : maps) {
for (Map.Entry<Integer, List<String>> e : map.entrySet()) {
combined.computeIfAbsent(e.getKey(), k -> new ArrayList<>()).addAll(e.getValue());
}
}
If you prefer a streams approach
Map<Integer, List<String>> m1;
Map<Integer, List<String>> m2;
Map<Integer, List<String>> m3;
Map<Integer, List<String>> combined = new HashMap<>();
Stream
.of(m1, m2, m3)
.flatMap(m -> m.entrySet().stream())
.forEach(e -> combined.computeIfAbsent(e.getKey(), k -> new ArrayList<>())
.addAll(e.getValue()));
I know you asked for a map, but based on your explanation your ultimate solution and the fact that your keys are now sequential integers starting at 0, you could create just a List<List<String>>. In that case, you could do it like this:
List<List<String>> result = mapList.stream()
.flatMap(map->map.values().stream())
.collect(Collectors.toList());

Java 8 Map not being sorted by value properly [duplicate]

This question already has answers here:
Java 8 is not maintaining the order while grouping
(2 answers)
Stream doesn't preserve the order after grouping
(3 answers)
Closed 4 years ago.
I've read many questions regarding Java 8 and Collections on this site and, given my limited understanding of java streams, I'm having some trouble trying to sort this Map.
My code is as follows, being tradeList an ArrayList();
Map<String, Double> buyReport = tradeList.stream().filter(trade -> trade.getInstruction() == Instruction.BUY)
.sorted(Comparator.comparing(trade -> trade.getInstructionDate()))
.collect(Collectors.groupingBy(trade -> dateFormat.format(trade.getInstructionDate()),
Collectors.summingDouble(trade -> trade.getUSDAmount())));
Does it make any sense to include the .sorted() statement when composing a HashMap? I tried to create a LinkedHashmap, use a custom comparator for the value i need the object instances to compare (a Double), but to no avail.
I can include any other piece of code you may find useful.
Thanks in advance!!
Update: tried this recently; still getting results unordered when grouping by company code and then summing company totals:
Map<String, Double> entityOutgoingReport = tradeList.stream()
.filter(trade -> trade.getInstruction() == Instruction.SELL)
.collect(Collectors.groupingBy(trade -> String.valueOf(trade.getEntity()),
LinkedHashMap::new,
Collectors.summingDouble(trade -> trade.getUSDAmount())));
for (String entity : entityOutgoingReport.keySet()) {
String key = entity;
String value = decFormat.format(entityOutgoingReport.get(entity));
tableBuilder.addRow(key, value); //Just pretty printing to console
}
Simply supply a LinkedHashMap into which the results will be inserted therefore maintaining order.
.collect(Collectors.groupingBy(trade ->
dateFormat.format(trade.getInstructionDate()),
LinkedHashMap::new,
Collectors.summingDouble(trade -> trade.getUSDAmount())));
Full code:
Map<String, Double> entityOutgoingReport =
tradeList.stream()
.filter(trade -> trade.getInstruction() == Instruction.SELL)
.sorted(Comparator.comparing(trade -> trade.getInstructionDate()))
.collect(Collectors.groupingBy(trade -> String.valueOf(trade.getEntity()),
LinkedHashMap::new,
Collectors.summingDouble(trade -> trade.getUSDAmount())));

How to sort a HashMap first by value and then by key in case of duplicates? [duplicate]

This question already has answers here:
Sorting LinkedHashMap
(4 answers)
Sort a Map<Key, Value> by values
(64 answers)
Closed 5 years ago.
I have a HashMap similar to this:
HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
map.put(3, 2);
map.put(7, 2);
map.put(5, 1);
map.put(10, 4);
I need to sort it first by value and then by key in case multiple keys share the same value.
The result should look like this:
(5, 1)
(3, 2)
(7, 2)
(10, 4)
Any suggestion please?
I'm comparing the values first and the keys only in case of a duplicate value. So I'm using using both the keys and the values, not just the values.
You can achieve it like so with the streams API:
LinkedHashMap<Integer, Integer> resultSet =
map.entrySet().stream()
.sorted(Map.Entry.<Integer, Integer>comparingByValue()
.thenComparing(Map.Entry.comparingByKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(oldValue, newValue) -> oldValue, LinkedHashMap::new));
The sorted intermediate operation can take a Comparator instance which in this case is:
Map.Entry.<Integer, Integer>comparingByValue()
.thenComparing(Map.Entry.comparingByKey())
i.e. compare by the map values and if two given values are the same then compare by the key.
Moving on to the collect terminal operation:
Map.Entry::getKey is the keyMapper which is a mapping function to produce the map keys.
Map.Entry::getValue is the valueMapper which is a mapping function to produce the map values.
(oldValue, newValue) -> oldValue is the merge function used to resolve collisions between values associated with the same key.
LinkedHashMap::new provides a new empty Map into which the results will be inserted. We specified a LinkedHashMap here to maintain insertion order.
Note the merge function (oldValue, newValue) -> oldValue is pointless in this case as the source of the stream (map.entrySet()) will never contain duplicate keys. However, we still need it otherwise we cannot specify the type of accumulator for the new values, in this case, a LinkedHashMap instance to maintain insertion order.
You may also want to look at the toMap method for further information on how it works.

How to add values from Map<T,List<L>> map to List<L>? [duplicate]

This question already has answers here:
Convert List of List into list in java
(5 answers)
Closed 5 years ago.
I have a multimap Map<T,List<L>> map and I need a list with all the values of the values from the map, namely List<L>. With map.values() I get a List<List<L>>, but thats not what I want.
Does someone know a clean solution without looping?
If you are using Java 8, you could collect all L values from all List<L>s in a single List<L> by Stream#flatMap:
final List<L> list = map
// get a Collection<List<L>>
.values()
// make a stream from the collection
.stream()
// turn each List<L> into a Stream<L> and merge these streams
.flatMap(List::stream)
// accumulate the result into a List
.collect(Collectors.toList());
Otherwise, a for-each approach with Collection#addAll can be applied:
final List<L> list = new ArrayList<>();
for (final List<L> values : map.values()) {
list.addAll(values);
}

Categories