Java 有序映射

Posted

技术标签:

【中文标题】Java 有序映射【英文标题】:Java Ordered Map 【发布时间】:2010-10-14 09:13:35 【问题描述】:

在 Java 中,是否有一个对象像 Map 一样用于存储和访问键/值对,但可以返回键的有序列表和值的有序列表,使得键和值列表在同一个订购?

因此,作为代码解释,我正在寻找类似于我虚构的 OrderedMap 的东西:

OrderedMap<Integer, String> om = new OrderedMap<>();
om.put(0, "Zero");
om.put(7, "Seven");

String o = om.get(7); // o is "Seven"
List<Integer> keys = om.getKeys();
List<String> values = om.getValues();

for(int i = 0; i < keys.size(); i++)

    Integer key = keys.get(i);
    String value = values.get(i);
    Assert(om.get(key) == value);

【问题讨论】:

如果您只想同时遍历两者,那么 Map.entrySet() 将允许您在任何地图上执行此操作。 LinkedHashMap 具有明确定义的顺序,但对于任何 Map,条目集都反映了键/值对。 此代码不是一个很好的示例,因为任何 Map 实现都会像您的示例代码一样。排序,排序与否。 在 Sun JDK 实现中,getKeys 和 getValues() 返回的集合由 map 中的 entrySet() 支持,因此将具有相同的迭代顺序,这就是您的示例测试。 这很有趣,我从来没有注意到这一点。尽管如此,还是说我疯了,但我不想对接口未明确验证的实现做出假设。过去我被烧伤太多次了。 这应该被命名为 Java Sorted Map,因为 Ordered Map 是不同的 - 见LinkedHashMap 【参考方案1】:

SortedMap 接口(带有实现TreeMap)应该是你的朋友。

接口有方法:

keySet() 按升序返回一组键 values() 以对应键的升序返回所有值的集合

所以这个界面完全符合您的要求。但是,键必须具有有意义的顺序。否则,您可以使用LinkedHashMap,其中的顺序由插入顺序决定。

【讨论】:

示例:SortedMap map = new TreeMap(); 要使用 TreeMap 需要关键类必须实现 Comparable 接口。如果没有,那么会抛出某种 RuntimeException。 TreeMap 它也是排序地图,但我认为作者想使用刚刚排序(未排序)的地图。 LinkedHashMap 只获取有序映射是不错的选择(正如您所说,“由插入顺序确定”)。 这个答案可以通过展示如何迭代 keySet() 来改进 来自java 8 doc。 LinkedHashMap 的迭代顺序是最后一次访问其条目的顺序 @TRiNE 我不关注您的评论,但我可能错过了一些上下文。默认情况下LinkedHashMap 的迭代顺序是插入顺序,但您可以使用不同的构造函数来指定访问顺序。 docs.oracle.com/javase/8/docs/api/java/util/…【参考方案2】:

是否有一个对象可以像 Map 一样存储和访问键/值对,但可以返回键的有序列表和值的有序列表,使得键和值列表的顺序相同?

您正在寻找java.util.LinkedHashMap。您将获得Map.Entry<K,V> 对的列表,它们总是以相同的顺序迭代。该顺序与您放入项目的顺序相同。或者,使用java.util.SortedMap,其中键必须具有自然顺序,或者由Comparator 指定。

【讨论】:

为了避免读者仔细检查这一点,因为很难通过测试来验证,keySet() 方法有效地返回一个 LinkedHashSet,它反映了您的 put() 调用的顺序。请注意,重复调用 put() 以获得相同的密钥不会改变顺序,除非您事先 remove() 密钥。 @TRiNE 引用链接文档:“...这通常是键插入地图的顺序”。迭代顺序始终是“插入顺序”,除非您使用special constructor 要求“访问顺序”【参考方案3】:

LinkedHashMap 维护键的顺序。

java.util.LinkedHashMap 看起来就像普通的 HashMap 一样工作。

【讨论】:

这没有提供问题的答案。要批评或要求作者澄清,请在他们的帖子下方发表评论 - 您可以随时评论自己的帖子,一旦您有足够的reputation,您就可以comment on any post。 @ianaya89 我认为这是一个真实的答案,但它与John Feminella's 答案非常相似! 如果你想得到一个有序的地图,当你把它们放入地图时,条目是按照这个顺序存储的,那么 LinkedHashMap 是正确的答案。如果您想按照放置它们的顺序对地图中的条目进行排序,那么 SortedMap 是正确的答案。 @TRiNE 您链接的 java 8 文档说有两种可能的排序模式:插入顺序和访问顺序。您考虑使用特殊构造函数 public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 调用的后者。默认构造函数创建一个按插入顺序排列的 LinkedHashMap 实例。 @ArturŁysik 是的。这是我几天前犯的一个错误。我会纠正它。我正在删除评论。因为我不能再编辑它了【参考方案4】:

我认为您将从框架中获得的最接近的集合是 SortedMap

【讨论】:

如果我认为值得为此丢分,我会否决这个答案。正如上面的答案所指出的,你的答案缺乏关于 LinkedHashMap 的正确信息,对 SortedMap 做一点解释也很好。 @CorayThan,在这种情况下,您赞成最好的答案,而不是反对可能正确但不是最好的其他答案... 我就是这么做的。只是说我能理解为什么有人会投反对票。【参考方案5】:

您可以利用NavigableMap 接口,该接口可以按升序或降序键顺序访问和遍历。这个接口是intended to supersede SortedMap 接口。 Navigable 地图通常根据其键的自然顺序或地图创建时提供的 Comparator 进行排序。

它有三个最有用的实现:TreeMap、ImmutableSortedMap 和 ConcurrentSkipListMap。

树图示例:

TreeMap<String, Integer> users = new TreeMap<String, Integer>();
users.put("Bob", 1);
users.put("Alice", 2);
users.put("John", 3);

for (String key: users.keySet()) 
  System.out.println(key + " (ID = "+ users.get(key) + ")");

输出:

Alice (ID = 2)
Bob (ID = 1)
John (ID = 3)

【讨论】:

【参考方案6】:

我认为 SortedMap 接口强制执行您的要求,而 TreeMap 实现了它。

http://java.sun.com/j2se/1.5.0/docs/api/java/util/SortedMap.html http://java.sun.com/j2se/1.5.0/docs/api/java/util/TreeMap.html

【讨论】:

【参考方案7】:

从 Java 6 开始,TreeMap 也有了非阻塞线程安全的替代方案。 见ConcurrentSkipListMap。

【讨论】:

【参考方案8】:

tl;博士

要使Map&lt; Integer , String &gt; 保持按键排序的顺序,请使用实现SortedMap/NavigableMap 接口的两个类之一:

TreeMap ConcurrentSkipListMap

… 或第三方实现。也许在Google GuavaEclipse Collections 中(我没有检查过)。

如果在单个线程中操作地图,请使用第一个线程,TreeMap。如果跨线程操作,请使用第二个,ConcurrentSkipListMap

有关详细信息,请参阅下表和以下讨论。

详情

这是我制作的图表,显示了与 Java 11 捆绑的十个 Map 实现的特性。

NavigableMap 接口是SortedMap 的继承者。 SortedMap 在逻辑上应该被删除,但不能因为某些第 3 方地图实现可能正在使用接口。

正如您在此表中看到的,只有两个类实现了SortedMap/NavigableMap 接口:

TreeMap ConcurrentSkipListMap

这两者都按自然顺序(使用Comparable(https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Comparable.html) 接口的compareTo 方法)或通过您传递的Comparator 实现保持键的排序顺序。这两个类的区别在于第二个类ConcurrentSkipListMap是thread-safe,高度是concurrent。

另请参阅下表中的迭代顺序列。

LinkedHashMap 类按照最初插入的顺序返回其条目。 EnumMap键的枚举类定义的顺序返回条目。例如,哪个员工覆盖一周中的哪一天 (Map&lt; DayOfWeek , Person &gt;) 的地图使用 Java 中内置的 DayOfWeek 枚举类。该枚举定义为第一个星期一和最后一个星期日。因此迭代器中的条目将按该顺序出现。

其他六个实现不承诺它们报告条目的顺序。

【讨论】:

【参考方案9】:

我使用Simple Hash map、链表和Collections按值对Map进行排序。

import java.util.*;
import java.util.Map.*;
public class Solution 

    public static void main(String[] args) 
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<String, Integer>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("javascript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Entry<String, Integer>> list = new LinkedList<Entry<String, Integer>>(map.entrySet());
        // sort the linked list using Collections.sort()
        Collections.sort(list, new Comparator<Entry<String, Integer>>()
        @Override
         public int compare(Entry<String, Integer> m1, Entry<String, Integer> m2) 
        return m1.getValue().compareTo(m2.getValue());
        
      );
      for(Entry<String, Integer> value: list) 
         System.out.println(value);
     
   

输出是:

C=0
C++=1
Java=2
Python=3
JavaScript=4
Golang=5

【讨论】:

【参考方案10】:

Steffi Keran 答案的现代 Java 版本

public class Solution 
    public static void main(String[] args) 
        // create a simple hash map and insert some key-value pairs into it
        Map<String, Integer> map = new HashMap<>();
        map.put("Python", 3);
        map.put("C", 0);
        map.put("JavaScript", 4);
        map.put("C++", 1);
        map.put("Golang", 5);
        map.put("Java", 2);
        // Create a linked list from the above map entries
        List<Map.Entry<String, Integer>> list = new LinkedList<>(map.entrySet());
        // sort the linked list using Collections.sort()
        list.sort(Comparator.comparing(Map.Entry::getValue));
        list.forEach(System.out::println);
    

【讨论】:

以上是关于Java 有序映射的主要内容,如果未能解决你的问题,请参考以下文章

java中的集合与映射

用Java实现桶排序算法

如何获得有序集合/有序映射的最大值和最小值?

使用 NHibernate 的双连接有序树映射

Java的Map集合中多个不同的key可以映射到同一个value吗?

python 将YAML映射加载为有序字典