【问题标题】:Does an ArrayList created from the keySet() of LinkedHashMap preserves the order of insertion?从 LinkedHashMap 的 keySet() 创建的 ArrayList 是否保留插入顺序?
【发布时间】:2015-09-29 19:51:11
【问题描述】:

我得到类似的数据

{"ABC 员工 1", "ABCX"}, {"ABC 员工 2", "ABCY"}, {"ABC 员工 3", "ABCZ"}

从数据库通过RefCursor。

我有一个案例,我需要保留从数据库中读取数据的顺序。 由于我的数据是一种“键值”,我想到了使用有序的Map 的实现。因此选择了LinkedHashMap

//defined as a static map inside a common utlity class
public class Common{
public static final LinkedHashMap<String, String> empMap = new   LinkedHashMap<String, String>();
}

//rs is the resultset
if (rs != null) {
            while (rs.next()) {
                key = rs.getString("name_text");
                value = rs.getString("id");
                Common.empMap.put(key, value);
            }
}

我必须按照从数据库(光标)中检索密钥的相同顺序将密钥传递给客户端。

List<String> empList = new ArrayList<String>(Common.empMap.keySet());

keySet() - 文档说“返回键的集合视图 包含在这张地图中。该集合由地图支持,因此更改为 地图反映在集合中,反之亦然”

我期望的是,由于ArrayList 也是一个有序集合,我应该以与检索/插入Map 相同的方式获取密钥。

当我做一个示例测试程序时,我得到了预期的结果。

public class LinkedMap {    
    public static void main(String[] args) {
        LinkedHashMap<String, String> map = new LinkedHashMap<String, String>();        
        map.put("Employee 1 of ABC", "ABCX");
        map.put("Employee 2 of ABC", "ABCY");
        map.put("Employee 3 of ABC", "ABCZ");   
        ArrayList<String> list = new ArrayList<String>(map.keySet());   
        System.out.println(list);   
    }
}

输出:[ABC 员工 1,ABC 员工 2,ABC 员工 3]

但是我的问题是,如果这是保证输出还是我只是随机得到的,它可能会有所不同(?),

  • 2019 年 9 月 14 日更新

我将答案与问题分开以避免任何混淆。

【问题讨论】:

  • 我喜欢变量类型是最具体的LinkedHashMap,而不是模糊的Map :)
  • 我认为,拥有接口或具体的类引用对我的用例没有影响。但我必须承认,使用具体类而不是接口是错误的。还是你觉得不一样?
  • 不,我认为最好对本地/实例变量使用最具体的类型。

标签: java linkedhashmap


【解决方案1】:

它是保证的。

尽管Set 接口本身不保证任何顺序(好吧,LinkedHashSet 确实如此),Map 实现本身保证插入顺序这一事实几乎可以保证您将获得其中的键订购。 .keySet() 返回的接口恰好是 Set,因为 Map 中的键保证是唯一的。

如果不是,请考虑在这种情况下会发生什么:

// case 1
for (final Map.Entry<K, V> entry: map.entrySet()) {
    // entry.getKey(), entry.getValue()
}

// case 2
for (final K key: map.keySet()) {
    V value = map.get(key);
}

如果这两个代码有两种不同的行为,呃……

【讨论】:

  • 谢谢 +1,“.keySet() 返回的接口恰好是一个 Set” - 想到这个 Set 让我很困惑
  • 好吧,如果这两段代码以不同的顺序迭代会怎样?甚至 .values()?
【解决方案2】:

是的,这是有保证的。

在 Oracle JDK 中,类LinkedHashMap 重新实现了方法newKeyIterator(),该方法返回一个继承LinkedHashIterator 的类的实例。那个是订的。

【讨论】:

  • 非常有效的一个,+1 我潜入其中看到了这个:)
【解决方案3】:

其他答案说明LinkedHashMap#keySet().iterator() 按指定顺序迭代,所以让我补充一下:

ArrayListJavadoc 说关于ArrayList(Collection):

按照集合的迭代器返回的顺序构造一个包含指定集合元素的列表。

因此可以保证ArrayList 项的顺序相同。

【讨论】:

    【解决方案4】:

    LinkedHashSet 的迭代顺序保证与插入键的顺序相同(假设您不使用允许您通过最近访问请求排序的特殊构造函数)。你可以在its documentation找到这个。

    我想您可能会争辩说,迭代顺序保证仅适用于条目集,而不适用于其他视图,但文档不能很好地支持这一点(因为它们引用了 迭代顺序),并且在实践中,集合视图确实共享一个共同的迭代顺序。

    就其本身而言,构造函数ArrayList(Collection) is documented 按照指定Collection 的迭代器返回元素的顺序填充列表。

    【讨论】:

      【解决方案5】:

      正如这个问题Does entrySet() in a LinkedHashMap also guarantee order? 中的解释和JavaDocs 中所指出的,迭代类型操作被定义为LinkedHashMap 的插入顺序。这会影响keySet 和entrySet。

      ArrayList 的构造函数将按照迭代器的顺序插入List。

      这两个条件结合起来,意味着 API 保证了这种行为。

      【讨论】:

      • 好一个,参考 +1
      【解决方案6】:

      谢谢大家,你们每个人都贡献了有效的积分。

      总结所有答案,

      保证订单维持。

      根据Javadocs,

      LinkedHashMap是Hash表和链表的Map实现 接口,具有可预测的迭代顺序。这个实现 与 HashMap 的不同之处在于它维护了一个双向链表运行 通过其所有条目。这个链表定义了迭代 排序,通常是键插入的顺序 地图(插入顺序)

      ie:LinkedHashMap 将按照条目放入地图的顺序进行迭代。

      所以LinkedHashMap#keySet() 会给我与将键插入地图相同的顺序,因为 LinkedHashMap#keySet().iterator() 按指定顺序迭代。

      深入到iterator()的实现我们可以看到,

      LinkedHashMap 实现了 newKeyIterator() 方法,该方法返回一个继承 LinkedHashIterator 的类的实例,负责“排序”

      // These Overrides alter the behavior of superclass view iterator() methods
      Iterator<K> newKeyIterator()   { return new KeyIterator();   } ...  
      
      private class KeyIterator extends LinkedHashIterator<K> {...
      
      private abstract class LinkedHashIterator<T> implements Iterator<T> { ...
      

      为此:ArrayList&lt;String&gt; list = new ArrayList&lt;String&gt;(map.keySet()); 构造函数 ArrayList(Collection) 是 documented 以按照指定 Collection 的迭代器返回元素的顺序填充列表。

      【讨论】:

        猜你喜欢
        • 2010-10-14
        • 1970-01-01
        • 1970-01-01
        • 2010-11-14
        • 1970-01-01
        • 2014-02-01
        • 2014-12-17
        • 2019-11-28
        • 1970-01-01
        相关资源
        最近更新 更多