Answers:
对象之间没有区别;HashMap<String, Object>
在两种情况下,您都有一个。与对象之间的接口有所不同。在第一种情况下,接口为HashMap<String, Object>
,而在第二种情况下为Map<String, Object>
。但是底层对象是相同的。
使用的好处Map<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
s开头,因为我认为这是编写类时要使用的适当结构。
后来,玛丽编写了将其子类化的代码。她与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...
}
后来,我决定实际上,最好使用TreeMap
而不是HashMap
in Foo
。我更新Foo
,更改HashMap
为TreeMap
。现在,SpecialFoo
不再编译了,因为我违反了合同:Foo
曾经说它提供了HashMap
s,但是现在提供了TreeMaps
。因此,我们必须立即修复SpecialFoo
(这种情况可能会在代码库中引起涟漪)。
除非我有一个很好的理由要分享我的实现正在使用HashMap
(并且确实发生了),否则我应该做的就是声明getThings
并getMoreThings
返回Map<String, Object>
而没有比这更具体的内容。实际上,除非有充分的理由做其他事情,甚至在Foo
我可能应该将things
和声明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...
}
请注意,我现在正在如何尽可能地使用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
编译停止。
接口(和基类)使我们仅显示必要的内容,因此可以灵活地进行更改。总的来说,我们希望参考文献尽可能基本。如果我们不需要知道它是a HashMap
,则称它为a Map
。
这不是盲目的规则,但总的来说,与最特定的接口编码相比,对最通用的接口进行编码将不那么困难。如果我还记得这一点,那我就不会创建一个Foo
将Mary设置为失败的SpecialFoo
。如果Mary记得这一点,那么即使我搞砸了Foo
,她也会用Map
而不是声明她的私有方法,而HashMap
我更改Foo
的合同不会影响她的代码。
有时候你做不到,有时候你必须要具体。但是除非有理由,否则请针对最不特定的界面。
地图具有以下实现:
哈希图 Map m = new HashMap();
LinkedHashMap Map m = new LinkedHashMap();
树图 Map m = new TreeMap();
WeakHashMap Map m = new WeakHashMap();
假设您创建了一个方法(这只是伪代码)。
public void HashMap getMap(){
return map;
}
假设您的项目需求发生了变化:
HashMap
。 HashMap
为LinkedHashMap
。 LinkedHashMap
为TreeMap
。 如果您的方法返回特定的类而不是实现Map
接口的某种方法,则您getMap()
每次必须更改方法的返回类型。
但是,如果您使用Java的多态性功能,而不是返回特定的类,请使用interface Map
,它可以提高代码的可重用性并减少需求变更的影响。
我只是想以此作为对已接受答案的评论,但是它太时髦了(我讨厌没有换行符)
嗯,所以区别在于,Map通常具有与之关联的某些方法。但是创建地图的方式不同,例如HashMap,这些不同的方式提供了并非所有地图都具有的独特方法。
的确如此-并且您始终希望使用可能的最通用的界面。考虑ArrayList与LinkedList。使用它们的方式差异很大,但是如果使用“列表”,则可以在它们之间轻松切换。
实际上,您可以使用更具动态性的语句替换初始化程序的右侧。这样的事情怎么样:
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
这样,如果您要使用插入排序填充集合,则可以使用链表(将插入排序插入数组列表是犯罪的。)但是,如果您不需要保持其排序而只需要追加,您使用ArrayList(对于其他操作更有效)。
这是一个很大的扩展,因为集合不是最好的示例,但是在OO设计中,最重要的概念之一是使用界面外观以完全相同的代码访问不同的对象。
编辑回应评论:
至于下面的地图注释,“是”使用“地图”界面会将您限制为仅那些方法,除非您将集合从地图强制回退到HashMap(这完全违背了目的)。
通常,您要做的是创建对象并使用某种特定类型(HashMap)将其填充,以某种“创建”或“初始化”方法进行,但是该方法将返回不需要不再作为HashMap进行操作。
如果您必须强制转换,则可能是使用了错误的接口,或者您的代码结构不够好。请注意,让您的代码的一部分将其视为“ HashMap”是可以接受的,而另一部分则将其视为“ Map”是可以接受的,但是这应该顺其自然。这样你就永远不会投降。
还要注意接口指示的角色的半简洁方面。LinkedList可以构成一个良好的堆栈或队列,ArrayList可以构成一个良好的堆栈,但是一个可怕的队列(同样,移除队列会导致整个列表移位),因此LinkedList实现了Queue接口,而ArrayList则没有。
Map是地图的静态类型,而HashMap是地图的动态类型。这意味着编译器会将您的地图对象视为Map类型之一,即使在运行时它可能指向它的任何子类型。
这种针对接口而不是实现进行编程的做法的另一个好处是保持灵活性:例如,您可以在运行时替换地图的动态类型,只要它是Map的子类型(例如LinkedHashMap),然后更改地图的行为即可。苍蝇。
一个好的经验法则是在API级别上保持尽可能抽象:例如,如果您正在编程的方法必须在地图上工作,则将参数声明为Map而不是更严格的(因为不那么抽象)HashMap类型就足够了。这样,您的API使用者就可以灵活地选择将哪种Map实现传递给您的方法。
除了最受投票支持的答案之外,还有很多人强调“更通用,更好”,我想再多谈一点。
Map
是结构合同,而HashMap
实现则提供了自己的方法来处理各种实际问题:如何计算索引,容量是多少,如何递增,如何插入,如何保持索引唯一等。
让我们看一下源代码:
在Map
我们有方法containsKey(Object key)
:
boolean containsKey(Object key);
JavaDoc:
boolean java.util.Map.containsValue(对象值)
如果此映射将一个或多个键映射到指定值,则返回true。更正式地说,当且仅当此映射至少包含一个映射到一个值,返回true
v
这样(value==null ? v==null : value.equals(v))
。对于Map接口的大多数实现,此操作可能需要地图大小中的时间线性。参数:值
其在此地图中的存在将被证明的值
返回值:true
如果此映射将一个或多个键映射到指定的
valueThrows:
ClassCastException-如果该值的类型与此地图不适当(可选)
NullPointerException-如果指定的值为null,并且此映射不允许使用null值(可选)
它需要它的实现来实现,但是“如何做”是自由的,只是为了确保它返回正确。
在HashMap
:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
事实证明,HashMap
使用哈希码测试此映射是否包含密钥。因此它具有哈希算法的优势。
Map是接口,Hashmap是实现Map Interface的类
Map是Interface,而Hashmap是实现该接口的类。
因此,在此实现中,您将创建相同的对象
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
首先Map
是它有不同的实现等的接口- ,,HashMap
等接口就像为实现类超类。因此,根据OOP的规则,实现的任何具体类也是一个。这意味着我们可以将任何类型变量分配/放置到类型变量,而无需任何类型的转换。TreeHashMap
LinkedHashMap
Map
Map
HashMap
Map
在这种情况下,我们可以分配map1
给map2
而不进行任何强制转换或任何数据丢失-
map2 = map1