我创建的下列Map之间有什么区别(在另一个问题中,人们的回答似乎是互换的,我想知道它们是否/如何不同):
HashMap<String, Object> map = new HashMap<String, Object>(); Map<String, Object> map = new HashMap<String, Object>();
bksxznpy1#
map是hashmap实现的接口。区别在于,在第二个实现中,对hashmap的引用只允许使用map接口中定义的函数,而第一个实现将允许使用hashmap中的任何公共函数(包括map接口)。如果您阅读sun的界面教程,可能会更有意义
nxagd54h2#
map是map的静态类型,hashmap是map的动态类型。这意味着编译器将把map对象视为map类型,即使在运行时,它可能指向它的任何子类型。这种针对接口而不是实现进行编程的做法还有一个额外的好处,那就是保持灵活性:例如,您可以在运行时替换map的动态类型,只要它是map的子类型(例如linkedhashmap),并动态更改map的行为。一个好的经验法则是在api级别保持尽可能的抽象性:例如,如果您正在编程的方法必须在map上工作,那么将参数声明为map就足够了,而不是更严格(因为抽象性较低)的hashmap类型。这样,api的使用者就可以灵活地决定要传递给方法的Map实现的类型。
2ledvvac3#
在第二个示例中,“map”引用的类型是 Map ,这是由实现的接口 HashMap (和其他类型的 Map ). 这个接口是一个约定,表示对象将键Map到值,并支持各种操作(例如。 put , get ). 它没有提到执行该计划的问题 Map (在本例中为a) HashMap ).第二种方法通常是首选的,因为您通常不希望将特定的map实现公开给使用 Map 或者通过api定义。
Map
HashMap
put
get
9q78igpj4#
对象之间没有区别;你有一个 HashMap<String, Object> 在这两种情况下。你与对象的接口不同。在第一种情况下,接口是 HashMap<String, Object> 而在第二种情况下 Map<String, Object> . 但底层对象是相同的。使用的优点 Map<String, Object> 您可以将基础对象更改为不同类型的Map,而不必违背与使用它的任何代码的约定。如果你宣布它是 HashMap<String, Object> ,如果要更改基础实现,则必须更改合同。例如:假设我写了这个类:
HashMap<String, Object>
Map<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的内部Map,它(通过访问器方法)与子类共享这些Map。假设我是用 HashMap 首先是因为我认为这是编写类时使用的适当结构。后来,mary编写了它的子类代码。她两者都有关系 things 以及 moreThings ,所以她很自然地把它放在一个普通的方法中,她使用的类型和我用的相同 getThings / getMoreThings 在定义她的方法时:
things
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 在 Foo . 我更新 Foo ,正在更改 HashMap 至 TreeMap . 现在, SpecialFoo 不再编译了,因为我违反了合同: Foo 过去常说它提供了 HashMap s、 但现在它提供了 TreeMaps 相反。所以我们必须修复 SpecialFoo 现在(这种事情可以通过一个代码库产生涟漪)。除非我有很好的理由分享我的实现使用了 HashMap (这确实发生了),我应该做的是宣布 getThings 以及 getMoreThings 就像刚刚回来一样 Map<String, Object> 没有比这更具体的了。事实上,除非有充分的理由去做别的事情,即使是在 Foo 我应该宣布 things 以及 moreThings 作为 Map ,不是 HashMap / TreeMap :
TreeMap
Foo
SpecialFoo
TreeMaps
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 停止编译。接口(和基类)允许我们只显示必要的内容,保持我们的灵活性,以便根据需要进行更改。总的来说,我们希望我们的参考文献尽可能基本。如果我们不需要知道 HashMap ,就称之为 Map .这不是一个盲目的规则,但一般来说,编码到最通用的接口要比编码到更具体的接口容易。如果我记得的话,我就不会创造一个 Foo 这使玛丽注定要失败 SpecialFoo . 如果玛丽记得的话,那么即使我搞砸了 Foo ,她会用 Map 而不是 HashMap 还有我的零钱 Foo 的合同不会影响她的代码。有时候你做不到,有时候你必须具体点。但是,除非你有理由这样做,否则就要朝着最不具体的接口走。
5kgi1eie5#
我只是想对接受的答案做一个评论,但它太时髦了(我讨厌没有换行符)啊,所以区别在于,一般来说,map有一些方法与之相关。但是创建Map有不同的方法,例如hashmap,这些不同的方法提供了并非所有Map都具有的独特方法。没错——而且您总是希望尽可能使用最通用的接口。考虑arraylist和linkedlist。你使用它们的方式有很大的不同,但是如果你使用“列表”,你可以很容易地在它们之间切换。实际上,您可以用更动态的语句替换初始值设定项的右侧。这样怎么样:
List collection; if(keepSorted) collection=new LinkedList(); else collection=new ArrayList();
这样,如果要用插入排序填充集合,可以使用链表(在数组列表中插入排序是违法的),但如果不需要保持排序,只需追加,则可以使用arraylist(对于其他操作更有效)。这是一个相当大的延伸,因为集合不是最好的例子,但在oo设计中,最重要的概念之一是使用接口facade使用完全相同的代码访问不同的对象。编辑回复评论:至于下面的map注解,yes使用“map”接口将您限制为只使用那些方法,除非您将集合从map转换回hashmap(这完全违背了目的)。通常,您要做的是创建一个对象,并使用它的特定类型(hashmap)在某种“create”或“initialize”方法中填充它,但该方法将返回一个“map”,不再需要作为hashmap进行操作。如果您必须顺便转换,那么您可能使用了错误的接口,或者您的代码结构不够好。请注意,让代码的一部分将其视为“hashmap”,而另一部分将其视为“map”是可以接受的,但这应该是“向下”的。这样你就再也不会铸造了。还要注意接口表示的角色的半整洁方面。linkedlist生成了一个好的堆栈或队列,arraylist生成了一个好的堆栈,但却是一个可怕的队列(同样,删除会导致整个列表的移动),因此linkedlist实现了队列接口,arraylist没有。
ykejflvf6#
map有以下实现:哈希图 Map m = new HashMap(); linkedhashmap公司 Map m = new LinkedHashMap(); 树形图 Map m = new TreeMap(); 懦夫Map Map m = new WeakHashMap(); 假设您已经创建了一个方法(这只是伪代码)。
Map m = new HashMap();
Map m = new LinkedHashMap();
Map m = new TreeMap();
Map m = new WeakHashMap();
public void HashMap getMap(){ return map; }
假设您的项目需求发生变化:方法应该返回Map内容-需要返回 HashMap .该方法应按插入顺序返回Map键-需要更改返回类型 HashMap 至 LinkedHashMap .方法应该按排序顺序返回Map键-需要更改返回类型吗 LinkedHashMap 至 TreeMap .如果您的方法返回特定的类而不是实现 Map 接口,则必须更改 getMap() 方法每次。但是如果您使用java的多态性特性,而不是返回特定的类,请使用接口 Map 它提高了代码的可重用性,减少了需求变化的影响。
LinkedHashMap
getMap()
sf6xfgos7#
正如tj crowder和adamski所指出的,一个引用是接口,另一个引用是接口的特定实现。根据joshua block的说法,您应该始终尝试编写接口代码,以便更好地处理对底层实现的更改—即,如果hashmap突然不适合您的解决方案,并且您需要更改map实现,那么您仍然可以使用map接口,并更改示例化类型。
nxagd54h8#
再加上投票率最高的答案和上面强调“更通用,更好”的许多答案,我想再多挖掘一点。 Map 结构是合同吗 HashMap 一个实现提供了自己的方法来处理不同的实际问题:如何计算索引、容量是多少以及如何增加它、如何插入、如何保持索引的唯一性等等。让我们看看源代码:在 Map 我们有办法 containsKey(Object key) :
containsKey(Object key)
boolean containsKey(Object key);
javadoc公司:boolean java.util.map.containsvalue(对象值)如果此Map将一个或多个键Map到指定值,则返回true。更正式地说,当且仅当此Map至少包含一个到值的Map时,返回true v 以至于 (value==null ? v==null : value.equals(v)) . 对于map接口的大多数实现,此操作可能需要Map大小的线性时间。parameters:value在该Map中显示的值returns:true如果此Map将一个或多个键Map到指定的值抛出:classcastexception-如果值的类型不适合此Map(可选)nullpointerexception-如果指定的值为null并且此Map不允许null值(可选)它需要它
v
(value==null ? v==null : value.equals(v))
8条答案
按热度按时间bksxznpy1#
map是hashmap实现的接口。区别在于,在第二个实现中,对hashmap的引用只允许使用map接口中定义的函数,而第一个实现将允许使用hashmap中的任何公共函数(包括map接口)。
如果您阅读sun的界面教程,可能会更有意义
nxagd54h2#
map是map的静态类型,hashmap是map的动态类型。这意味着编译器将把map对象视为map类型,即使在运行时,它可能指向它的任何子类型。
这种针对接口而不是实现进行编程的做法还有一个额外的好处,那就是保持灵活性:例如,您可以在运行时替换map的动态类型,只要它是map的子类型(例如linkedhashmap),并动态更改map的行为。
一个好的经验法则是在api级别保持尽可能的抽象性:例如,如果您正在编程的方法必须在map上工作,那么将参数声明为map就足够了,而不是更严格(因为抽象性较低)的hashmap类型。这样,api的使用者就可以灵活地决定要传递给方法的Map实现的类型。
2ledvvac3#
在第二个示例中,“map”引用的类型是
Map
,这是由实现的接口HashMap
(和其他类型的Map
). 这个接口是一个约定,表示对象将键Map到值,并支持各种操作(例如。put
,get
). 它没有提到执行该计划的问题Map
(在本例中为a)HashMap
).第二种方法通常是首选的,因为您通常不希望将特定的map实现公开给使用
Map
或者通过api定义。9q78igpj4#
对象之间没有区别;你有一个
HashMap<String, Object>
在这两种情况下。你与对象的接口不同。在第一种情况下,接口是HashMap<String, Object>
而在第二种情况下Map<String, Object>
. 但底层对象是相同的。使用的优点
Map<String, Object>
您可以将基础对象更改为不同类型的Map,而不必违背与使用它的任何代码的约定。如果你宣布它是HashMap<String, Object>
,如果要更改基础实现,则必须更改合同。例如:假设我写了这个类:
这个类有几个string->object的内部Map,它(通过访问器方法)与子类共享这些Map。假设我是用
HashMap
首先是因为我认为这是编写类时使用的适当结构。后来,mary编写了它的子类代码。她两者都有关系
things
以及moreThings
,所以她很自然地把它放在一个普通的方法中,她使用的类型和我用的相同getThings
/getMoreThings
在定义她的方法时:后来,我决定,实际上,如果我用
TreeMap
而不是HashMap
在Foo
. 我更新Foo
,正在更改HashMap
至TreeMap
. 现在,SpecialFoo
不再编译了,因为我违反了合同:Foo
过去常说它提供了HashMap
s、 但现在它提供了TreeMaps
相反。所以我们必须修复SpecialFoo
现在(这种事情可以通过一个代码库产生涟漪)。除非我有很好的理由分享我的实现使用了
HashMap
(这确实发生了),我应该做的是宣布getThings
以及getMoreThings
就像刚刚回来一样Map<String, Object>
没有比这更具体的了。事实上,除非有充分的理由去做别的事情,即使是在Foo
我应该宣布things
以及moreThings
作为Map
,不是HashMap
/TreeMap
:注意我现在是如何使用
Map<String, Object>
我能做到的每一个地方,只有在我创建实际对象时才是具体的。如果我这么做了,那么玛丽就会这么做:
…和改变
Foo
不会让你SpecialFoo
停止编译。接口(和基类)允许我们只显示必要的内容,保持我们的灵活性,以便根据需要进行更改。总的来说,我们希望我们的参考文献尽可能基本。如果我们不需要知道
HashMap
,就称之为Map
.这不是一个盲目的规则,但一般来说,编码到最通用的接口要比编码到更具体的接口容易。如果我记得的话,我就不会创造一个
Foo
这使玛丽注定要失败SpecialFoo
. 如果玛丽记得的话,那么即使我搞砸了Foo
,她会用Map
而不是HashMap
还有我的零钱Foo
的合同不会影响她的代码。有时候你做不到,有时候你必须具体点。但是,除非你有理由这样做,否则就要朝着最不具体的接口走。
5kgi1eie5#
我只是想对接受的答案做一个评论,但它太时髦了(我讨厌没有换行符)
啊,所以区别在于,一般来说,map有一些方法与之相关。但是创建Map有不同的方法,例如hashmap,这些不同的方法提供了并非所有Map都具有的独特方法。
没错——而且您总是希望尽可能使用最通用的接口。考虑arraylist和linkedlist。你使用它们的方式有很大的不同,但是如果你使用“列表”,你可以很容易地在它们之间切换。
实际上,您可以用更动态的语句替换初始值设定项的右侧。这样怎么样:
这样,如果要用插入排序填充集合,可以使用链表(在数组列表中插入排序是违法的),但如果不需要保持排序,只需追加,则可以使用arraylist(对于其他操作更有效)。
这是一个相当大的延伸,因为集合不是最好的例子,但在oo设计中,最重要的概念之一是使用接口facade使用完全相同的代码访问不同的对象。
编辑回复评论:
至于下面的map注解,yes使用“map”接口将您限制为只使用那些方法,除非您将集合从map转换回hashmap(这完全违背了目的)。
通常,您要做的是创建一个对象,并使用它的特定类型(hashmap)在某种“create”或“initialize”方法中填充它,但该方法将返回一个“map”,不再需要作为hashmap进行操作。
如果您必须顺便转换,那么您可能使用了错误的接口,或者您的代码结构不够好。请注意,让代码的一部分将其视为“hashmap”,而另一部分将其视为“map”是可以接受的,但这应该是“向下”的。这样你就再也不会铸造了。
还要注意接口表示的角色的半整洁方面。linkedlist生成了一个好的堆栈或队列,arraylist生成了一个好的堆栈,但却是一个可怕的队列(同样,删除会导致整个列表的移动),因此linkedlist实现了队列接口,arraylist没有。
ykejflvf6#
map有以下实现:
哈希图
Map m = new HashMap();
linkedhashmap公司Map m = new LinkedHashMap();
树形图Map m = new TreeMap();
懦夫MapMap m = new WeakHashMap();
假设您已经创建了一个方法(这只是伪代码)。假设您的项目需求发生变化:
方法应该返回Map内容-需要返回
HashMap
.该方法应按插入顺序返回Map键-需要更改返回类型
HashMap
至LinkedHashMap
.方法应该按排序顺序返回Map键-需要更改返回类型吗
LinkedHashMap
至TreeMap
.如果您的方法返回特定的类而不是实现
Map
接口,则必须更改getMap()
方法每次。但是如果您使用java的多态性特性,而不是返回特定的类,请使用接口
Map
它提高了代码的可重用性,减少了需求变化的影响。sf6xfgos7#
正如tj crowder和adamski所指出的,一个引用是接口,另一个引用是接口的特定实现。根据joshua block的说法,您应该始终尝试编写接口代码,以便更好地处理对底层实现的更改—即,如果hashmap突然不适合您的解决方案,并且您需要更改map实现,那么您仍然可以使用map接口,并更改示例化类型。
nxagd54h8#
再加上投票率最高的答案和上面强调“更通用,更好”的许多答案,我想再多挖掘一点。
Map
结构是合同吗HashMap
一个实现提供了自己的方法来处理不同的实际问题:如何计算索引、容量是多少以及如何增加它、如何插入、如何保持索引的唯一性等等。让我们看看源代码:
在
Map
我们有办法containsKey(Object key)
:javadoc公司:
boolean java.util.map.containsvalue(对象值)
如果此Map将一个或多个键Map到指定值,则返回true。更正式地说,当且仅当此Map至少包含一个到值的Map时,返回true
v
以至于(value==null ? v==null : value.equals(v))
. 对于map接口的大多数实现,此操作可能需要Map大小的线性时间。parameters:value
在该Map中显示的值
returns:true
如果此Map将一个或多个键Map到指定的
值抛出:
classcastexception-如果值的类型不适合此Map(可选)
nullpointerexception-如果指定的值为null并且此Map不允许null值(可选)
它需要它