小编典典

Java中的HashMap和Map对象有什么区别?

all

我创建的以下地图有什么区别(在另一个问题中,人们似乎可以互换使用它们的回答,我想知道它们是否/如何不同):

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();

阅读 122

收藏
2022-03-24

共1个答案

小编典典

对象之间没有区别;在这两种情况下你都有一个HashMap<String, Object>。您对对象的 接口
有所不同。在第一种情况下,接口是HashMap<String, Object>,而在第二种情况下是Map<String, Object>.
但底层对象是相同的。

使用它的好处Map<String, Object>是,您可以将底层对象更改为不同类型的映射,而不会违反使用它的任何代码的合同。如果将其声明为HashMap<String, Object>,则如果要更改底层实现,则必须更改合同。


示例:假设我编写了这个类:

class Foo {
    private HashMap<String, Object> things;
    private HashMap<String, Object> moreThings;

    protected HashMap<String, Object> getThings() {
        return this.things;
    }

    protected HashMap<String, Object> getMoreThings() {
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

该类有几个字符串->对象的内部映射,它与子类共享(通过访问器方法)。假设我以HashMaps 开头,因为我认为这是编写类时使用的适当结构。

后来,Mary
编写了代码子类化它。她有一些事情需要处理thingsmoreThings,所以她很自然地将其放在一个通用方法中,并且她使用了我在定义她的方法时getThings/使用的相同类型:getMoreThings

class SpecialFoo extends Foo {
    private void doSomething(HashMap<String, Object> t) {
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }

    // ...more...
}

后来,我决定实际上,如果我使用TreeMap而不是HashMapin会更好Foo。我更新Foo,更改HashMapTreeMap.
现在,SpecialFoo不再编译,因为我违反了合同:Foo以前说它提供了HashMaps,但现在它TreeMaps改为提供。所以我们现在必须修复SpecialFoo(这种事情可能会波及代码库)。

除非我有充分的理由分享我的实现正在使用 a
HashMap(并且确实发生了),否则我应该做的是声明getThings并且getMoreThings只是返回Map<String, Object>而没有比这更具体的内容。事实上,除非有充分的理由去做其他事情,即使在Foo我可能应该声明thingsand
moreThingsas Map,而不是HashMap/ TreeMap

class Foo {
    private Map<String, Object> things;             // <== Changed
    private Map<String, Object> moreThings;         // <== Changed

    protected Map<String, Object> getThings() {     // <== Changed
        return this.things;
    }

    protected Map<String, Object> getMoreThings() { // <== Changed
        return this.moreThings;
    }

    public Foo() {
        this.things = new HashMap<String, Object>();
        this.moreThings = new HashMap<String, Object>();
    }

    // ...more...
}

请注意我现在是如何在Map<String, Object>任何我能使用的地方使用的,只有在我创建实际对象时才具体说明。

如果我这样做了,那么玛丽会这样做:

class SpecialFoo extends Foo {
    private void doSomething(Map<String, Object> t) { // <== Changed
        // ...
    }

    public void whatever() {
        this.doSomething(this.getThings());
        this.doSomething(this.getMoreThings());
    }
}

…并且更改Foo不会SpecialFoo停止编译。

接口(和基类)让我们 只展示必要 的内容,同时保持我们的灵活性以进行适当的更改。一般来说,我们希望我们的参考文献尽可能基本。如果我们不需要知道它是 a
HashMap,就称它为 a Map

这不是一个盲目的规则,但总的来说, 对最通用的接口 进行编码要比对更具体的接口进行编码更容易。如果我记得这一点,我就不会创建一个Foo让 Mary
SpecialFoo. 如果 Mary 记得这一点,那么即使我搞砸了Foo,她也会声明她的私有方法
withMap而不是,HashMap并且我更改Foo的合同不会影响她的代码。

有时你不能这样做,有时你必须具体。但是,除非你有理由这样做,否则会错误地选择最不具体的界面。

2022-03-24