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

我建立的如下地图之间有什么区别(在另外一个问题中,人们彷佛能够互换使用它们,我想知道它们是否/如何不一样): this

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

#1楼

您建立相同的地图。 编码

可是您能够在使用时弥补差别。 在第一种状况下,您将可以使用特殊的HashMap方法(但我不记得任何人真的有用),而且您能够将其做为HashMap参数传递: spa

public void foo (HashMap<String, Object) { ... }

...

HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;

foo (m1);
foo ((HashMap<String, Object>)m2);

#2楼

对象之间没有区别; 在两种状况下HashMap<String, Object>您都有HashMap<String, Object> 。 与对象之间的接口有所不一样。 在第一种状况下,接口是HashMap<String, Object> ,而在第二种状况下,接口是Map<String, Object> 。 可是底层对象是相同的。 code

使用Map<String, Object>的优势在于,您能够将基础对象更改成另外一种类型的映射,而不会违反使用该映射的任何代码的约定。 若是将其声明为HashMap<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...
}

该类有一些string-> object的内部映射,它与子类共享(经过访问器方法)。 假设我首先使用HashMap编写它,由于我认为这是编写类时要使用的适当结构。 ci

后来,玛丽编写了将其子类化的代码。 她须要同时处理thingsmoreThings ,所以天然而然地将其放在一个通用方法中,而且在定义她的方法时,她使用与getThings / getMoreThings相同的类型: get

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

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

    // ...more...
}

稍后,我决定实际上,最好是在Foo使用TreeMap而不是HashMap 。 我更新Foo ,将HashMap更改成TreeMap 。 如今, SpecialFoo再也不编译,由于我违反了合同: Foo曾经说它提供了HashMap ,可是如今提供了TreeMaps 。 所以,咱们如今必须修复SpecialFoo (这种事情可能会在代码库中引发涟漪)。 string

除非我有一个很好的理由要分享个人实现正在使用HashMap (而且确实发生了),不然我应该作的就是将getThingsgetMoreThings声明为仅返回Map<String, Object>而没有比这更具体的声明。 实际上,除非有充分的理由作其余事情,即便在Foo我也应该将thingsmoreThings things声明为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中止编译。

接口(和基类)使咱们显示必要的内容,所以能够灵活地进行更改。 总的来讲,咱们但愿参考文献尽量基本。 若是咱们不须要知道它是HashMap ,只需将其称为Map

这不是一个盲目的规则,但总的来讲, 与最特定的接口编码相比,对最通用的接口进行编码将不那么困难。 若是我还记得这一点,那么我不会建立一个让Mary因SpecialFoo失败而建立的Foo 。 若是Mary记得这一点,那么即便我搞砸了Foo ,她也会使用Map而不是HashMap声明她的私有方法,而且我更改Foo的合同不会影响她的代码。

有时候你作不到,有时候你必需要具体。 可是除非有理由,不然请针对最不特定的界面。


#3楼

在第二个示例中,“ map”引用的类型为Map ,这是由HashMap (以及其余类型的Map )实现的接口。 这个接口是一个约定,表示对象将键映射到值并支持各类操做(例如putget )。 它没有提到 Map 的实现 (在本例中为HashMap )。

一般首选第二种方法,由于您一般不但愿使用Map或经过API定义将特定的Map实现公开给方法。


#4楼

Map是Interface,而Hashmap是实现该接口的类。

所以,在此实现中,您将建立相同的对象


#5楼

正如TJ Crowder和Adamski所指出的,一种参考是接口,另外一种是接口的特定实现。 根据Joshua Block的说法,您应该始终尝试对接口进行编码,以使您可以更好地处理对基础实现的更改-即,若是HashMap忽然对于您的解决方案不理想,而且您须要更改Map的实现,那么您仍然可使用Map接口,并更改实例化类型。

相关文章
相关标签/搜索