Java 中的 HashMap 和 Map 对象有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1348199/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the difference between the HashMap and Map objects in Java?
提问by Tony Stark
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>();
采纳答案by T.J. Crowder
There is no difference between the objects; you have a HashMap<String, Object>
in both cases. There is a difference in the interfaceyou 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.
对象之间没有区别;HashMap<String, Object>
在这两种情况下你都有。对象的接口有所不同。在第一种情况下,接口是HashMap<String, Object>
,而在第二种情况下是Map<String, Object>
。但底层对象是相同的。
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.
使用的好处Map<String, Object>
是您可以将底层对象更改为不同类型的地图,而不会破坏与使用它的任何代码的合同。如果您将其声明为HashMap<String, Object>
,则如果您想更改底层实现,则必须更改您的合同。
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.
该类有几个 string->object 的内部映射,它与子类共享(通过访问器方法)。假设我用HashMap
s 开始编写它,因为我认为这是编写类时使用的适当结构。
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:
后来,Mary 编写了子类化它的代码。她需要对things
and做一些事情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());
}
// ...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).
后来,我决定实际上,最好使用TreeMap
而不是HashMap
in Foo
。我更新Foo
,更改HashMap
为TreeMap
. 现在,SpecialFoo
不再编译了,因为我违反了合同:Foo
过去说它提供了HashMap
s,但现在它提供了TreeMaps
。所以我们现在必须修复SpecialFoo
(这种事情可能会影响代码库)。
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
:
除非我有一个很好的理由来分享我的实现正在使用 a HashMap
(并且确实发生了),否则我应该做的是声明getThings
并getMoreThings
返回Map<String, Object>
而不是更具体。事实上,除非有充分的理由去做其他事情,即使在Foo
我可能应该声明things
and moreThings
as 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...
}
Note how I'm now using Map<String, Object>
everywhere I can, only being specific when I create the actual objects.
请注意我现在如何尽可能地使用Map<String, Object>
,只有在我创建实际对象时才具体。
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.
...并且更改Foo
不会SpecialFoo
停止编译。
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
.
接口(和基类)让我们只透露必要的信息,保持我们的灵活性以进行适当的更改。一般来说,我们希望我们的参考资料尽可能基本。如果我们不需要知道它是 a HashMap
,只需将其称为 a Map
。
This isn't a blind rule, but in general, coding to the most general interfaceis 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 Maryhad 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.
这不是一个盲目的规则,但一般来说,对最通用的界面进行编码将比对更具体的内容进行编码要容易些。如果我记得这一点,我就不会创建一个Foo
让 Mary 因SpecialFoo
. 如果Mary记得这一点,那么即使我搞砸了Foo
,她也会用Map
而不是声明她的私有方法,HashMap
并且我更改Foo
的合同不会影响她的代码。
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.
有时你不能这样做,有时你必须具体。但是除非您有理由这样做,否则请错误地使用最不具体的界面。
回答by Roman
You create the same maps.
您创建相同的地图。
But you can fill the difference when you will use it. With first case you'll be able to use special HashMap methods (but I don't remember anyone realy useful), and you'll be able to pass it as a HashMap parameter:
但是您可以在使用它时填补差异。在第一种情况下,您将能够使用特殊的 HashMap 方法(但我不记得任何真正有用的方法),并且您将能够将其作为 HashMap 参数传递:
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
回答by Adamski
In your second example the "map" reference is of type Map
, which is an interface implemented by HashMap
(and other types of Map
). This interface is a contractsaying that the object maps keys to values and supports various operations (e.g. put
, get
). It says nothing about the implementationof the Map
(in this case a HashMap
).
在您的第二个示例中,“地图”引用的类型为Map
,它是由HashMap
(和其他类型的Map
)实现的接口。这个接口是一个约定,表示对象将键映射到值并支持各种操作(例如put
,get
)。它说,任何关于实施的Map
(在这种情况下HashMap
)。
The second approach is generally preferred as you typically wouldn't want to expose the specific map implementation to methods using the Map
or via an API definition.
第二种方法通常是首选,因为您通常不希望使用Map
API 定义或通过 API 定义将特定映射实现公开给方法。
回答by Diego Dias
Map is the Interface and Hashmap is the class that implements that.
Map 是接口,Hashmap 是实现它的类。
So in this implementation you create the same objects
所以在这个实现中你创建了相同的对象
回答by aperkins
As noted by TJ Crowder and Adamski, one reference is to an interface, the other to a specific implementation of the interface. According to Joshua Block, you should always attempt to code to interfaces, to allow you to better handle changes to underlying implementation - i.e. if HashMap suddenly was not ideal for your solution and you needed to change the map implementation, you could still use the Map interface, and change the instantiation type.
正如 TJ Crowder 和 Adamski 所指出的,一个引用是接口,另一个引用是接口的特定实现。根据 Joshua Block 的说法,您应该始终尝试对接口进行编码,以便更好地处理对底层实现的更改 - 即如果 HashMap 突然不适合您的解决方案并且您需要更改地图实现,您仍然可以使用 Map接口,并更改实例化类型。
回答by Graphics Noob
Mapis an interface that HashMapimplements. The difference is that in the second implementation your reference to the HashMap will only allow the use of functions defined in the Map interface, while the first will allow the use of any public functions in HashMap (which includes the Map interface).
Map是HashMap实现的一个接口。不同之处在于,在第二个实现中,您对 HashMap 的引用仅允许使用 Map 接口中定义的函数,而第一个实现将允许使用 HashMap(包括 Map 接口)中的任何公共函数。
It will probably make more sense if you read Sun's interface tutorial
如果您阅读Sun 的界面教程,可能会更有意义
回答by Matthias
Map is the static typeof map, while HashMap is the dynamic typeof map. This means that the compiler will treat your map object as being one of type Map, even though at runtime, it may point to any subtype of it.
Map是map的静态类型,而HashMap是map的动态类型。这意味着编译器会将您的 map 对象视为 Map 类型之一,即使在运行时,它可能指向它的任何子类型。
This practice of programming against interfaces instead of implementations has the added benefit of remaining flexible: You can for instance replace the dynamic type of map at runtime, as long as it is a subtype of Map (e.g. LinkedHashMap), and change the map's behavior on the fly.
这种针对接口而不是实现编程的做法具有保持灵活性的额外好处:例如,您可以在运行时替换动态类型的映射,只要它是 Map 的子类型(例如 LinkedHashMap),并更改映射的行为苍蝇。
A good rule of thumb is to remain as abstract as possible on the API level: If for instance a method you are programming must work on maps, then it's sufficient to declare a parameter as Map instead of the stricter (because less abstract) HashMap type. That way, the consumer of your API can be flexible about what kind of Map implementation they want to pass to your method.
一个好的经验法则是在 API 级别保持尽可能抽象:例如,如果您正在编程的方法必须在映射上工作,那么将参数声明为 Map 而不是更严格(因为不那么抽象)的 HashMap 类型就足够了. 这样,您的 API 的使用者就可以灵活地决定他们想要传递给您的方法的 Map 实现类型。
回答by Bill K
I was just going to do this as a comment on the accepted answer but it got too funky (I hate not having line breaks)
我只是打算这样做作为对已接受答案的评论,但它太时髦了(我讨厌没有换行符)
ah, so the difference is that in general, Map has certain methods associated with it. but there are different ways or creating a map, such as a HashMap, and these different ways provide unique methods that not all maps have.
啊,所以不同的是,一般情况下,Map有一些与之相关的方法。但是有不同的方法或创建地图,例如 HashMap,这些不同的方法提供了并非所有地图都具有的独特方法。
Exactly--and you always want to use the most general interface you possibly can. Consider ArrayList vs LinkedList. Huge difference in how you use them, but if you use "List" you can switch between them readily.
没错——而且您总是希望尽可能使用最通用的界面。考虑 ArrayList 与 LinkedList。您使用它们的方式存在巨大差异,但如果您使用“列表”,您可以轻松地在它们之间切换。
In fact, you can replace the right-hand side of the initializer with a more dynamic statement. how about something like this:
事实上,您可以用更动态的语句替换初始化程序的右侧。这样的事情怎么样:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
This way if you are going to fill in the collection with an insertion sort, you would use a linked list (an insertion sort into an array list is criminal.) But if you don't need to keep it sorted and are just appending, you use an ArrayList (More efficient for other operations).
这样,如果您要使用插入排序填充集合,您将使用链表(将插入排序插入数组列表是犯罪行为。)但是如果您不需要保持排序并且只是追加,您使用 ArrayList (对于其他操作更有效)。
This is a pretty big stretch here because collections aren't the best example, but in OO design one of the most important concepts is using the interface facade to access different objects with the exact same code.
这是一个相当大的延伸,因为集合不是最好的例子,但在 OO 设计中最重要的概念之一是使用接口外观来访问具有完全相同代码的不同对象。
Edit responding to comment:
编辑回复评论:
As for your map comment below, Yes using the "Map" interface restricts you to only those methods unless you cast the collection back from Map to HashMap (which COMPLETELY defeats the purpose).
至于您在下面的地图评论,是的,使用“地图”接口限制您只能使用这些方法,除非您将集合从 Map 转换回 HashMap (这完全违背了目的)。
Often what you will do is create an object and fill it in using it's specific type (HashMap), in some kind of "create" or "initialize" method, but that method will return a "Map" that doesn't need to be manipulated as a HashMap any more.
通常你会做的是创建一个对象并使用它的特定类型(HashMap)填充它,在某种“创建”或“初始化”方法中,但该方法将返回一个不需要的“Map”不再作为 HashMap 进行操作。
If you ever have to cast by the way, you are probably using the wrong interface or your code isn't structured well enough. Note that it is acceptable to have one section of your code treat it as a "HashMap" while the other treats it as a "Map", but this should flow "down". so that you are never casting.
顺便说一句,如果您不得不进行强制转换,那么您可能使用了错误的接口,或者您的代码结构不够好。请注意,让您的代码的一部分将其视为“HashMap”而另一部分将其视为“Map”是可以接受的,但这应该“向下”流动。所以你永远不会铸造。
Also notice the semi-neat aspect of roles indicated by interfaces. A LinkedList makes a good stack or queue, an ArrayList makes a good stack but a horrific queue (again, a remove would cause a shift of the entire list) so LinkedList implements the Queue interface, ArrayList does not.
还要注意接口指示的角色的半整洁方面。LinkedList 是一个很好的堆栈或队列,一个 ArrayList 是一个很好的堆栈但是一个可怕的队列(同样,删除会导致整个列表的移动)所以 LinkedList 实现了 Queue 接口,ArrayList 没有。
回答by kolyaseg
HashMap is an implementation of Map so it's quite the same but has "clone()" method as i see in reference guide))
HashMap 是 Map 的一种实现,因此它完全相同,但具有“clone()”方法,正如我在参考指南中看到的那样))
回答by Razib
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
First of all Map
is an interface it has different implementation like - HashMap
, TreeHashMap
, LinkedHashMap
etc. Interface works like a super class for the implementing class. So according to OOP's rule any concrete class that implements Map
is a Map
also. That means we can assign/put any HashMap
type variable to a Map
type variable without any type of casting.
首先Map
是它有不同的实现等的接口- ,,等接口就像为实现类超类。所以根据 OOP 的规则,任何实现的具体类也是一个。这意味着我们可以将任何类型变量分配/放入类型变量而无需任何类型的转换。HashMap
TreeHashMap
LinkedHashMap
Map
Map
HashMap
Map
In this case we can assign map1
to map2
without any casting or any losing of data -
在这种情况下,我们可以map1
在map2
不进行任何转换或丢失任何数据的情况下分配给-
map2 = map1