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,其中的顺序由插入顺序决定。
【讨论】:
示例:SortedMapLinkedHashMap
的迭代顺序是最后一次访问其条目的顺序
@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< Integer , String >
保持按键排序的顺序,请使用实现SortedMap
/NavigableMap
接口的两个类之一:
TreeMap
ConcurrentSkipListMap
… 或第三方实现。也许在Google Guava 或Eclipse 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< DayOfWeek , Person >
) 的地图使用 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 有序映射的主要内容,如果未能解决你的问题,请参考以下文章