通过键排序时,迭代HashMap的最佳方法

huangapple go评论74阅读模式
英文:

Best way to iterate through HashMap<Integer,X> when you need sorted by keys

问题

I need to convert HashMap entries into List of some DTOs(based both on key and value from map). I also need it to be sorted by keys.

I noticed that since my keys are Integers I can access it just through:

map.forEach(..);

and the order withholds. Is it the most efficient way? Or are there any better approaches?

In that case I care more about efficiency than clean code.

EDIT:
I do not want to use TreeMap because I also need O(1) and NOT O(Log(N)) for inserts.

英文:

I have a code as below:

private final HashMap&lt;Integer, X&gt; map = new HashMap&lt;&gt;();

I need to convert HashMap entries into List of some DTOs(based both on key and value from map). I also need it to be sorted by keys.

I noticed that since my keys are Integers I can access it just through:

map.forEach(..);

and the order witholds. Is it the most efficient way? Or are there any better apporaches?

In that case I care more about efficiency than clean code.

EDIT:
I do not want to use TreeMap because I also need O(1) and NOT O(Log(N) for inserts.

答案1

得分: 1

You said:

I noticed that since my keys are Integers I can access it just through:

map.forEach(..);

and the order withholds.

你提到,由于我的键是整数,我可以通过以下方式访问它:

map.forEach(..);

并且顺序保持不变。

That you saw a few numbers appear in sorted order was mere luck, not true sorting. The Map interface, and the HashMap class, clearly state in their Javadoc that they do not iterate with any particular order.

你看到一些数字以排序顺序出现只是纯粹的运气,而不是真正的排序。Map 接口和 HashMap 类在其 Javadoc 中明确声明它们不会按照特定顺序迭代。

That means you should not rely on any apparent ordering. Even if the current implementation did happen to use an ordering internally, the implementation is free to change in a future version to a different sort or arbitrary order, thereby breaking your app.

这意味着你不应该依赖于任何明显的顺序。即使当前实现确实在内部使用了某种顺序,但在将来的版本中,实现可以自由更改为不同的排序或任意顺序,从而破坏你的应用程序。

🔗 If you want sorted order, you must pay for sorting. You can either pay early or pay later, your choice.

🔗 如果你想要排序的顺序,你必须付费进行排序。 你可以选择提前付费或以后付费。

Pay early

提前付费,使用维护键顺序的 NavigableMap(或 SortedMap)。

TreeMap 类是这些接口的一种实现。如果需要并发性,ConcurrentSkipListMap 是另一种选择。

private final NavigableMap<Integer, X> map = new TreeMap<>();

Pay later

以后付费,首先填充你的 Map,比如 HashMap。当地图完成后,进行排序。

  • 你可以使用 Stream 进行排序,就像智能回答中的示例一样。
  • 或者你可以通过从你的 Map 构建一个 NavigableMap 来排序。
private final Map<Integer, X> map = new HashMap<>();

private final NavigableMap<Integer, X> navMap = new TreeMap<>(map);

一个完整的示例。

Map<Integer, String> map =
        Map.of(
                3, "Carol",
                1, "Alice",
                2, "Bob"
        );
NavigableMap<Integer, String> navMap = new TreeMap<>(map);

System.out.println("map = " + map);
System.out.println("navMap = " + navMap);

运行时,请注意 map 以不同于创建顺序的顺序打印。相反,navMap 始终按键排序顺序打印。

map = {2=Bob, 1=Alice, 3=Carol}
navMap = {1=Alice, 2=Bob, 3=Carol}

或者,在你的情况下,你正在生成一个你希望按照特定顺序的 List。你可以以后付费,首先以非排序顺序生成列表,然后对该列表进行排序。你可以按自然顺序排序,或者按照指定的 Comparator 进行排序

Collections.sort(list);
英文:

IYou said:

>I noticed that since my keys are Integers I can access it just through:
>
>map.forEach(..);
>
>and the order witholds.

That you saw a few numbers appear in sorted order was mere luck, not true sorting. The Map interface, and the HashMap class, clearly state in their Javadoc that they do not iterate with any particular order.

That means you should not rely on any apparent ordering. Even if the current implementation did happen to use an ordering internally, the implementation is free to change in a future version to a different sort or arbitrary order, thereby breaking your app.

👉 If you want sorted order, you must pay for sorting. You can either pay early or pay later, your choice.

Pay early

Pay early by using a NavigableMap (or SortedMap) that maintains your entries in the order of their keys.

The TreeMap class is one implementation of those interfaces. If you need concurrency, ConcurrentSkipListMap is another.

private final NavigableMap&lt;Integer, X&gt; map = new TreeMap&lt;&gt;();

Pay later

Pay later by first populating your Map such as HashMap. When the map is complete, sort.

  • You can sort by using Stream as seen in the smart Answer by WJS.
  • Or you can sort by building a NavigableMap from your Map.
private final Map&lt;Integer, X&gt; map = new HashMap&lt;&gt;();

private final NavigableMap&lt;Integer, X&gt; navMap = new TreeMap&lt;&gt;( map ) ;

A full example.

Map &lt; Integer, String &gt; map =
        Map.of(
                3 , &quot;Carol&quot; ,
                1 , &quot;Alice&quot; ,
                2 , &quot;Bob&quot;
        );
NavigableMap &lt; Integer, String &gt; navMap = new TreeMap &lt;&gt;( map );

System.out.println( &quot;map = &quot; + map );
System.out.println( &quot;navMap = &quot; + navMap );

When run, notice how map prints with an order different than creation order. In contrast, navMap always prints in key-sorted order.

map = {2=Bob, 1=Alice, 3=Carol}
navMap = {1=Alice, 2=Bob, 3=Carol}

Or, in your case, you are producing a List that you want in a certain order. You can pay later by producing the list in non-sorted order, then sort that list. You can either sort by natural order or sort by specifying a Comparator.

Collections.sort( list ) ;

You said:

>I do not want to use TreeMap because I also need O(1) and NOT O(Log(N) for inserts.

Beware of premature optimization. Unless you have huge numbers of elements, or frequently repeat this code, I would be surprised to find any significant impact. Perform some benchmarking or profiling rather than assume a performance bottleneck.

答案2

得分: 0

Sure, here is the translated code snippet:

// 需要将 HashMap 条目转换为某些 DTO 的列表(基于 map 的键和值)。还需要按键排序。
// 是否在插入期间使用 TreeMap 进行排序,或稍后排序取决于您,但仍然需要进行排序并产生成本。但为了回答您的问题,这里提供了一种替代方法。
// - 声明 DTO(这里我使用了一个记录)
// - 对 map 的 entrySet 进行流处理
// - 按键排序并创建每个 DTO,将它们返回到列表中。

record DTO<X>(Integer getInt, X getX) {}
Map<Integer, String> map = new HashMap<>(); // 用数据填充的 map
List<DTO<String>> list = map.entrySet().stream()
        .sorted(Entry.comparingByKey())
        .map(e -> new DTO<String>(e.getKey(), e.getValue()))
        .toList();
// 上述返回的列表将是不可变的。您还可以更改 ".toList()" 以返回特定类型的集合。这里是一个返回 ArrayList 的示例。
.collect(Collectors.toCollection(ArrayList::new));

Please note that the code itself remains in English, as programming code is often written in English regardless of the spoken language.

英文:

>I need to convert HashMap entries into List of some DTOs(based both on key and value from map). I also need it to be sorted by keys.

Whether you sort during insertion using a TreeMap, or later is up to you but you will still need to sort and incur the cost. But to address your question, here is an alternative.

  • declare the DTO (here I'm using a record)
  • stream the map entrySet
  • sort on the key create each DTO, return them in a list.
record DTO&lt;X&gt;(Integer getInt, X getX) {}
Map&lt;Integer, String&gt; map = new HashMap&lt;&gt;(); // populated with the data
List&lt;DTO&lt;String&gt;&gt; list = map.entrySet().stream()
        .sorted(Entry.comparingByKey())
        .map(e -&gt; new DTO&lt;String&gt;(e.getKey(), e.getValue()))
        .toList();

The above returned list will be immutable. You can also change .toList() to return a specific type of collection. Here is one returning an ArrayList.

.collect(Collectors.toCollection(ArrayList::new));

</details>



huangapple
  • 本文由 发表于 2023年4月16日 23:03:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/76028537.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定