【问题标题】:How to iterate on a hashmap of generics?如何迭代泛型的哈希图?
【发布时间】:2013-02-06 11:25:34
【问题描述】:

我有一个对象模型,它有一个父对象,我将在这里调用它 MyParent.java。该类有两个子类,名为 MySub1.java 和 MySub2.java。

我有两个 HashMap,一个包含 MySub1 实例,另一个包含 MySub2 实例。我正在尝试编写一段代码,它可以同时处理 HashMap 并执行完全相同的操作(稍后,拥有 MySub1 或 MySub2 实例会产生一些效果,但这里不会)。

这是编译的代码部分:

HashMap<String, ? extends MyParent> map = null;
if (some condition)
    map = // get a HashMap<String, MySub1>
if (some other condition)
    map = // get a HashMap<String, MySub2>

但现在,我不知道如何迭代 map 变量。我需要通过一个迭代器而不是对keySet() 的元素进行迭代,因为我需要从哈希图中删除一些元素。

这是我的代码中无法编译的部分:

if(map != null && map.size() > 0){
    Iterator<Entry<String, ? extends MyParent>> i = map.entrySet().iterator();
    while(i.hasNext()){
        Map.Entry<String, MyParent> pair = (Map.Entry<String, MyParent>) i.next();
        // get the object from the pair and handle it
    }
}

Eclipse 确实在我创建迭代器的行上打印了这条错误消息:

 cannot convert from Iterator<Map.Entry<String,capture#5-of ? extends MyParent>> to Iterator<Map.Entry<String,? extends MyParent>>

我真的没有收到错误消息。如果我将代码的第一行更改为:

HashMap<String, MyParent> map = null

现在不工作的代码确实可以工作,但工作代码不再工作,并出现此错误消息:

cannot convert from HashMap<String,MySub1> to HashMap<String,MyParent>

有什么想法吗?

【问题讨论】:

    标签: java generics iterator hashmap covariance


    【解决方案1】:

    您始终可以将 String 和 MyParent 的一对实例放入Map&lt;String,MyParent&gt;。您不能将该对放入Map&lt;String,MySub1&gt;。因此,无法从后者转换为前一个地图(如您最后一条错误消息所述)。另见wikipedia on covariance and contravariance

    相同问题的一个版本适用于迭代器。如果您的实际地图是Map&lt;String,MySub1&gt;,它会返回一个Iterator&lt;Map.Entry&lt;String,MySub1&gt;&gt;,它不能转换为Iterator&lt;Map.Entry&lt;String,? extends MyParent&gt;&gt;,即使Map.Entry&lt;String,MySub1&gt;Map.Entry&lt;String,? extends MyParent&gt;。例如,以下将编译:

    Map.Entry&lt;String,? extends MyParent&gt; someEntry = map.entrySet().iterator().next();

    另一个通配符 (?) 在这里有帮助:

    Iterator<? extends Map.Entry<String, ? extends MyParent>> i =  map.entrySet().iterator();
    

    【讨论】:

    • 您的代码可以编译(没有测试运行时)。但是你的修改不是我所期望的,我真的不明白......你能解释一下为什么需要? extends Map.Entry 吗?
    • 我认为您的部分解释在提交时被网站破坏了。您帖子中的两个Iterator&gt; 肯定是一段没有被反引号正确转义的代码。
    • @Wis 这也是我很难理解的。赋值左侧的? extends Something 不需要与右侧的? extends Something 引用完全相同的类型。如果你觉得上面的代码很难看,试着把“不能编译的代码”移到方法public &lt;T extends MyParent&gt; void removeSome(Map&lt;String,T&gt; map)中,并在里面使用Iterator&lt;Map.Entry&lt;String,T&gt;&gt;
    • @Wis 谢谢。希望已修复。
    • 感谢答案,泛型并不那么明显。总体概念很好,但细节有点棘手。顺便说一句,我的一位同事告诉我,泛型是一种在不需要时添加类型安全性的方法。他告诉我,他总是使用 Iterator i = map.entrySet().iterator() 而不使用任何类型声明,并添加了 @SuppressWarning 注释以消除编译器对未检查类型转换的警告。两者似乎在运行时都能完美运行。
    【解决方案2】:

    我不知道您的确切要求,但在同一个地图中拥有多个对象类型可能会造成严重的维护问题。希望你能想到以下场景

    1. 当两个条件都在稍后的某个时间点满足时

    2. 当 Mysub1/Mysub2 的行为发生变化时。

    3. 当其他人在维护代码时。

      我仍然建议使用单独的实例。为避免到处携带多个地图,您可能需要将它们包裹在一个对象中。

    【讨论】:

    • 这两个条件是互斥的,之前的测试已经验证了这两个条件中的一个是真的,而不是两个,而不是更少。我的课程实际上是没有任何逻辑的 DTO。只是一个将数据映射到数据库的容器。对于你的第三个注释,嗯......任何维护其他人代码的人总是觉得它很丑;)
    • 他在同一个地图中没有多个对象类型。他有一个仅包含 MySub1 值或仅包含 MySub2 值的映射,并且希望在基于没有特定子类属性的特定知识的情况下以相同的方法处理这两者,仅包含来自 MyParent 的那些。没有错。
    猜你喜欢
    • 2023-04-07
    • 2012-12-27
    • 2011-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-06
    • 2013-08-23
    相关资源
    最近更新 更多