Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to regroup a treemap with java streams

I have a TreeMap<Integer, Integer> instance and I want to reassign the key value mappings in that way that the lowest key is assigned to the lowest value and the highest key to the highest key.

Here is how I do it without streams:

 TreeMap<Integer, Integer> map = new TreeMap<>();
 map.put(1, 6);
 map.put(2, 9);
 map.put(4, 2);
 map.put(3, 1);
 map.put(8, 10);
 map.put(5, 10);

 ArrayList<Integer> valueList = new ArrayList<Integer>(map.values());
 Collections.sort(valueList);

 int i = 0;
 for (Map.Entry entry : map.entrySet()) {
      entry.setValue(valueList.get(i++));
 }

 System.out.println(map);

output:

{1=1, 2=2, 3=6, 4=9, 5=10, 8=10}

Any hints how to perform such a task utilizing java-8 Stream API are welcome.

Thx

like image 533
pero_hero Avatar asked Jan 01 '23 03:01

pero_hero


2 Answers

I have found out a solution that is fairly easy to read and use:

Iterator<Integer> keyIterator = map.keySet().iterator();
TreeMap<Integer, Integer> newMap = map.values().stream()
    .sorted()
    .map(value -> new SimpleEntry<>(keyIterator.next(), value))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (l, r) -> l, TreeMap::new)); 

.. or shorter thanks to @HadiJ:

map.values().stream()
            .sorted()
            .collect(Collectors.toMap(k -> keyIterator.next(),  Function.identity(), (l, r) -> l, TreeMap::new));

... but it has a significant drawback:

I cannot guarantee this will work in parallel since it depends on the result of keyIterator.next() which is also not checked. Read more at the section Stateless Behaviors. I'd rather not use java-stream in this way.


If I were you, I'd use the advantage of the beauty of iterators:

Iterator<Integer> values = valueList.iterator();
Iterator<Integer> keys = map.keySet().iterator();

TreeMap<Integer, Integer> newMap = new TreeMap<>();   // create a new Map
while (values.hasNext() && keys.hasNext()) {          // iterate simultaneously
    newMap.put(keys.next(), values.next());           // put the key-value
}
like image 52
Nikolas Charalambidis Avatar answered Jan 02 '23 18:01

Nikolas Charalambidis


Your approach is not bad. You can shorten it to

PriorityQueue<Integer> q = new PriorityQueue<>(map.values());
map.entrySet().forEach(e -> e.setValue(q.remove()));

I don't think that this task is a good candidate for the Stream API.

like image 28
Holger Avatar answered Jan 02 '23 16:01

Holger