Java 中的 HashMap 和 Map 对象有什么区别? [英] What is the difference between the HashMap and Map objects in Java?

查看:27
本文介绍了Java 中的 HashMap 和 Map 对象有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

What is the difference between the following maps I create (in another question, people answered using them seemingly interchangeably and I'm wondering if/how they are different):

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

推荐答案

对象之间没有区别;在这两种情况下,您都有一个 HashMap.对象的界面有所不同.在第一种情况下,接口是 HashMap,而在第二种情况下,它是 Map.但是底层对象是一样的.

There is no difference between the objects; you have a HashMap<String, Object> in both cases. There is a difference in the interface you have to the object. In the first case, the interface is HashMap<String, Object>, whereas in the second it's Map<String, Object>. But the underlying object is the same.

使用 Map 的优点是您可以将底层对象更改为不同类型的地图,而不会破坏与使用它的任何代码的合同.如果你将它声明为HashMap,如果你想改变底层实现,你必须改变你的契约.

The advantage to using Map<String, Object> is that you can change the underlying object to be a different kind of map without breaking your contract with any code that's using it. If you declare it as HashMap<String, Object>, you have to change your contract if you want to change the underlying implementation.

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

Example: Let's say I write this class:

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 开始编写它,因为我认为这是编写类时使用的适当结构.

The class has a couple of internal maps of string->object which it shares (via accessor methods) with subclasses. Let's say I write it with HashMaps to start with because I think that's the appropriate structure to use when writing the class.

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

Later, Mary writes code subclassing it. She has something she needs to do with both things and moreThings, so naturally she puts that in a common method, and she uses the same type I used on getThings/getMoreThings when defining her method:

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 过去说它提供了 HashMaps,但现在它提供 TreeMaps 代替.所以我们现在必须修复 SpecialFoo(这种事情可能会影响代码库).

Later, I decide that actually, it's better if I use TreeMap instead of HashMap in Foo. I update Foo, changing HashMap to TreeMap. Now, SpecialFoo doesn't compile anymore, because I've broken the contract: Foo used to say it provided HashMaps, but now it's providing TreeMaps instead. So we have to fix SpecialFoo now (and this kind of thing can ripple through a codebase).

除非我有充分的理由分享我的实现使用了 HashMap(而且确实发生了),否则我应该做的是声明 getThingsgetMoreThings 只是返回 Map 而没有比这更具体的.事实上,除非有很好的理由去做其他事情,即使在 Foo 中,我也应该将 thingsmoreThings 声明为 Map,而不是HashMap/TreeMap:

Unless I had a really good reason for sharing that my implementation was using a HashMap (and that does happen), what I should have done was declare getThings and getMoreThings as just returning Map<String, Object> without being any more specific than that. In fact, barring a good reason to do something else, even within Foo I should probably declare things and moreThings as Map, not 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 的,只有在我创建实际对象时才具体.

Note how I'm now using Map<String, Object> everywhere I can, only being specific when I create the actual objects.

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

If I had done that, then Mary would have done this:

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 停止编译.

...and changing Foo wouldn't have made SpecialFoo stop compiling.

接口(和基类)让我们只在必要时显示,保持我们的灵活性以进行适当的更改.一般来说,我们希望我们的参考资料尽可能基本.如果我们不需要知道它是一个HashMap,就叫它一个Map.

Interfaces (and base classes) let us reveal only as much as is necessary, keeping our flexibility under the covers to make changes as appropriate. In general, we want to have our references be as basic as possible. If we don't need to know it's a HashMap, just call it a Map.

这不是一个盲目的规则,但总的来说,编码到最通用的界面比编码到更具体的东西要容易得多.如果我记得这一点,我就不会创建一个 Foo 来设置 Mary 使用 SpecialFoo 失败.如果 Mary 记得这一点,那么即使我搞砸了 Foo,她也会用 Map 而不是 HashMap 声明她的私有方法 和我改变 Foo 的合同不会影响她的代码.

This isn't a blind rule, but in general, coding to the most general interface is going to be less brittle than coding to something more specific. If I'd remembered that, I wouldn't have created a Foo that set Mary up for failure with SpecialFoo. If Mary had remembered that, then even though I messed up Foo, she would have declared her private method with Map instead of HashMap and my changing Foo's contract wouldn't have impacted her code.

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

Sometimes you can't do that, sometimes you have to be specific. But unless you have a reason to be, err toward the least-specific interface.

这篇关于Java 中的 HashMap 和 Map 对象有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆