【问题标题】:HashMap values to CollectionHashMap 值到 Collection
【发布时间】:2016-06-01 19:07:23
【问题描述】:

我有以下代码:

    HashMap<Integer, String> h = new HashMap<Integer, String>();
    h.put(1, "a");
    h.put(2, "b");
    h.put(3, "c");
    h.put(4, "d");

    System.out.println(h); //{1=a, 2=b, 3=c, 4=d}

    Collection<String> vals = h.values();
    System.out.println(vals); //[a, b, c, d]

    Iterator<String> itr = vals.iterator();

    while (itr.hasNext()) //a b c d 
    {
        System.out.print(itr.next() + " ");
    }

我的问题:

  1. h.values() 返回h 中值的集合视图。既然vals是一个接口,我们怎么给接口赋值(不能实例化)?实现这个接口的类在哪里?该类的对象在哪里?

  2. itr 的类似问题。我们知道vals.iterator() 返回集合的第一个元素。我们如何将它分配给接口实例?

【问题讨论】:

  • 将其分配给接口实例”。听起来这是你的问题。 Collection&lt;String&gt; vals 没有声明实例,它声明了对Collection&lt;String&gt; 实现的引用。而且由于我们可以实现接口,因此问题不会出现。该实现(通常)在HashMap 内部。对于 OpenJDK,它是一个nested class,实现了Collection 接口。
  • 我刚刚尝试打印vals.getClass(),它打印了class java.util.HashMap$Values。那么这是否意味着这个类正在实现Collection&lt;String&gt; 接口?
  • 是的,当然。根据定义,如果一个变量是 Collection 类型,那么它引用了一个其类实现了 Collection 的对象。
  • @sam: 没错。例如,OpenJDK 实现扩展了AbstractCollection,它实现了Collection 接口,因此可以分配给该接口的任何(引用)变量。

标签: java collections hashmap iterator


【解决方案1】:

控制问题答案的基本原则称为Liskov's Substitution Principle,在这种情况下,它适用于将给定接口(@98​​7654322@、Iterator)的实例的值分配给其引用type 是实现该接口的类(例如AbstractCollection、一些匿名类等)。

如果您看到HashMap#values() 方法代码,您将在 Java 8 源代码中看到:

public Collection<V> values() {
    Collection<V> vs;
    return (vs = values) == null ? (values = new Values()) : vs;
}

final class Values extends AbstractCollection<V> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    ...
}

因此,您也被退回

  1. Values 类的实例,它扩展了实现CollectionAbstractCollection,或者
  2. AbstractCollection 的具体子类的实例(请参阅:values = new AbstractCollection&lt;V&gt;() at line 386 in AbstractMap.java in Java 8 源代码。

同样,根据 LSP,这都是有效的。要了解这一切是如何连接起来的,您需要深入了解 JDK 代码库。

更多答案

h.values() 返回 h 中值的 Collection 视图。既然 vals 是一个接口,我们如何给一个接口赋值(它们不能被实例化)?

准确地说,vals 不是一个接口,而是一个实例。确实,您不能使用 new 运算符(如 listener l = new Listener())来实例化一个接口,例如 Listener,但根据 LSP,您始终可以实例化 Listener 接口的具体实现并将其分配给一个变量,其类型为Listener,例如Listener listener = new SerialListener();

实现这个接口的类在哪里?

在这种情况下,它是Values 类或AbstractCollection 类,如上所示。

那个类的对象在哪里?

在某些情况下,它是匿名内部类的一个实例,在其定义时被实例化。

我们知道 vals.iterator() 返回集合的第一个元素。

不太对。它返回一个实现Iterator 接口的类的实例。如果并且当您对返回的对象调用 next() 方法时,您将获得集合的第一个元素(假设它不为空)。

我们如何将它分配给接口实例?

这个想法是一样的。如果赋值语句左侧的变量(= 符号的左侧)引用了一个接口,那么右侧可以引用对实现该接口的对象的引用,直接或间接(通过继承层次结构) )。

【讨论】:

    猜你喜欢
    • 2015-02-05
    • 1970-01-01
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-20
    • 2013-03-22
    相关资源
    最近更新 更多