Java中的HashMap和Map对象有什么区别? [英] What is the difference between the HashMap and Map objects in Java?
问题描述
我创建的以下地图有什么区别(在另一个问题上,人们使用它们看起来可以互换,我想知道/他们是如何不同的):
HashMap< String,Object> map = new HashMap< String,Object>();
映射< String,Object> map = new HashMap< String,Object>();
对象之间没有区别;在这两种情况下,您都有一个 HashMap< String,Object>
。您对该对象的界面有区别。在第一种情况下,接口是 HashMap< String,Object>
,而在第二种情况下,它是 Map< String,Object>
。但是基础对象是一样的。
使用 Map< String,Object>
的优点是可以将基础对象更改为不同类型的地图,而不会破坏与使用它的任何代码的合同。如果您将其声明为 HashMap< String,Object>
,则如果要更改基础实施,则必须更改合同。
示例:假设我写这个类:
class Foo {
private HashMap< String,Object>事情;
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>();
}
// ...更多...
}
该类有一些string-> object的内部映射,它通过子类与存储器方法共享。假设我用 HashMap
来写,因为我认为这是编写课程时使用的相应结构。
稍后,玛丽写代码进行子类化。她有一些与
的东西
和 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());
}
// ...更多...
}
以后,我确定实际上,如果我使用 TreeMap
而不是 HashMap
code>富。我更新 Foo
,将 HashMap
更改为 TreeMap
。现在, SpecialFoo
不再编译,因为我已经打破了合同: Foo
用于说它提供 HashMap
s,但现在它提供了 TreeMaps
。所以我们现在必须修复 SpecialFoo
(这种事情可以通过一个代码库)。
除非我有一个非常好的理由分享,我的实现使用一个 HashMap
(这确实发生),我应该做的是声明 getThings
和 getMoreThings
只返回 Map< String,Object>
而不再具体。实际上,除了在 Foo
之外,还有一个很好的理由,我应该声明的东西
和 moreThings
as Map
,而不是 HashMap
/ TreeMap
:
class Foo {
private Map< String,Object>事情; //< == Changed
private Map< String,Object> moreThings; //< ==更改
protected Map< String,Object> getThings(){//< ==更改
return this.things;
}
protected Map< String,Object> getMoreThings(){//< ==更改
返回this.moreThings;
}
public Foo(){
this.things = new HashMap< String,Object>();
this.moreThings = new HashMap< String,Object>();
}
// ...更多...
}
注意我现在如何使用 Map< String,Object>
无论我在哪里,只有在创建实际对象时才具体。
如果我这样做,那么玛丽会这样做:
class SpecialFoo扩展Foo {
private void doSomething(Map< String,Object> t){//< ==更改
// ...
}
public void whatever(){
this.doSomething(this.getThings());
this.doSomething(this.getMoreThings());
}
}
...并更改 Foo
不会使 SpecialFoo
停止编译。
接口(和基类)让我们仅仅发现必要的,保持我们的灵活性在适当的范围内进行更改。一般来说,我们希望让我们的参考文献尽可能的基本。如果我们不需要知道它是一个 HashMap
,只需将其称为 Map
。
这不是一个盲目的规则,但是一般来说,最普遍的接口编码对于更具体的编码而言比编码要脆弱得多。如果我记得,我不会创建一个 Foo
,设置Mary失败与 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; 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.
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 HashMap
s 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 HashMap
s, 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屋!