Java - HashMap与Map对象 [英] Java - HashMap vs Map objects

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

问题描述

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

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


解决方案

对象之间没有区别。在接口中有与对象的区别。在第一种情况下,接口是 HashMap ,而第二个是 Map< String,Object>



使用 Map< String,Object> 的优点是您可以将基础对象更改为另一种类型的映射,而不会使用任何使用它的代码打破合同。如果你声明为 HashMap< String,Object> ,如果你想改变底层的实现,你必须改变你的契约。






例如:我写这个类:

  Foo {
private HashMap< String,Object>东西
private HashMap< String,Object>更多

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>();
}

// ...更多...
}


b $ b

类有一对string-> object的内部映射,它与子类共享(通过访问器方法)。让我们说,我用 HashMap s来写它,因为我认为这是写类时使用的适当结构。



后来,玛丽写了代码子类化它。她需要处理事情 moreThings 的事情,所以她自然会用常见的方法,她在使用 getThings / getMoreThings 时使用的相同类型定义她的方法:



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

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

// ...更多...
}


b $ b

后来,我决定实际上,如果我使用 TreeMap 而不是 HashMap code> Foo 。我更新 Foo ,将 HashMap 更改为 TreeMap 。现在, SpecialFoo 不再编译,因为我已经违反了合同: Foo 用于说它提供 HashMap ,但现在提供 TreeMaps 。所以我们必须修复 SpecialFoo 现在(这种东西可以通过代码库波动)。



有一个非常好的理由共享,我的实现是使用 HashMap (这确实发生),我应该做的是声明 getThings getMoreThings ,因为只是返回 Map< String,Object> 事实上,即使在 Foo 中,我应该声明的东西 moreThings 作为映射,而不是 HashMap / TreeMap

  class Foo {
private Map private Map< String,Object>更多//< == 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>();
}

// ...更多...
}


b $ b

注意我现在使用 Map< String,Object> 到处可以,只有当我创建实际对象时才具体。



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

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

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

...并更改 Foo 不会使 SpecialFoo 停止编译。



接口)让我们只显示必要的 ,保持我们在封面下的灵活性,以适当地进行更改。一般来说,我们希望我们的引用尽可能基本。如果我们不需要知道它是一个 HashMap ,只需调用 Map



这不是一个盲目的规则,但一般来说,编码到最一般的接口比编码到更具体的东西脆弱。如果我记得,我不会创建一个 Foo 设置玛丽失败与 SpecialFoo 。如果玛丽记住了,那么即使我搞砸了 Foo ,她会用 Map 而不是 HashMap ,我更改的 Foo 的合同不会影响她的代码。



有时你不能这样做,有时你必须具体。但除非你有理由,否则向最不具体的界面走。


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>();

解决方案

There is no difference between the objects. 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>. The underlying object, though, is the same.

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...
}

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.

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...
}

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).

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...
}

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());
    }
}

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

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.

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天全站免登陆