简体   繁体   English

Java中的HashMap和Map对象之间有什么区别?

[英]What is the difference between the HashMap and Map objects in Java?

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. 在两种情况下HashMap<String, Object>您都有HashMap<String, Object> 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> . 在第一种情况下,接口是HashMap<String, Object> ,而在第二种情况下,接口是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. 使用Map<String, Object>的优点在于,您可以将基础对象更改为另一种类型的映射,而不会违反使用该映射的任何代码的约定。 If you declare it as HashMap<String, Object> , you have to change your contract if you want to change the underlying implementation. 如果将其声明为HashMap<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. 该类有一些string-> object的内部映射,它与子类共享(通过访问器方法)。 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. 假设我首先使用HashMap编写它,因为我认为这是编写类时要使用的适当结构。

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: 她需要同时处理thingsmoreThings ,因此自然而然地将其放在一个通用方法中,并且在定义她的方法时,她使用与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 . 稍后,我决定实际上,最好是在Foo使用TreeMap而不是HashMap I update Foo , changing HashMap to TreeMap . 我更新Foo ,将HashMap更改为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. 现在, SpecialFoo不再编译,因为我违反了合同: Foo曾经说它提供了HashMap ,但是现在提供了TreeMaps So we have to fix SpecialFoo now (and this kind of thing can ripple through a codebase). 因此,我们现在必须修复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. 除非我有一个很好的理由要分享我的实现正在使用HashMap (并且确实发生了),否则我应该做的就是将getThingsgetMoreThings声明为仅返回Map<String, Object>而没有比这更具体的声明。 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 : 实际上,除非有充分的理由做其他事情,即使在Foo我也应该将thingsmoreThings things声明为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 . 如果我们不需要知道它是HashMap ,只需将其称为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 . 如果我还记得这一点,那么我不会创建一个让Mary因SpecialFoo失败而创建的Foo 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. 如果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. 但是除非有理由,否则请针对最不特定的界面。

Map is an interface that HashMap implements. MapHashMap实现的接口。 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). 不同之处在于,在第二种实现中,您对HashMap的引用仅允许使用Map接口中定义的函数,而第一种将允许使用HashMap中的任何公共函数(包括Map接口)。

It will probably make more sense if you read Sun's interface tutorial 如果您阅读Sun的界面教程,可能会更有意义。

在此处输入图片说明

Map has the following implementations: 地图具有以下实现:

  1. HashMap Map m = new HashMap(); HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap(); LinkedHashMap Map m = new LinkedHashMap();

  3. Tree Map Map m = new TreeMap(); 树图Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap(); WeakHashMap Map m = new WeakHashMap();

Suppose you have created one method (this is just pseudocode). 假设您创建了一个方法(这只是伪代码)。

public void HashMap getMap(){
   return map;
}

Suppose your project requirements change: 假设您的项目需求发生了变化:

  1. The method should return map contents - Need to return HashMap . 该方法应返回地图内容-需要返回HashMap
  2. The method should return map key's in insertion order - Need to change return type HashMap to LinkedHashMap . 该方法应按插入顺序返回映射键-需要将返回类型HashMap更改为LinkedHashMap
  3. The method should return map key's in sorted order - Need to change return type LinkedHashMap to TreeMap . 该方法应按排序顺序返回映射键-需要将返回类型LinkedHashMap更改为TreeMap

If your method returns specific classes instead of something that implements the Map interface, you have to change the return type of getMap() method each time. 如果您的方法返回特定的类而不是实现Map接口的某些类,则必须每次更改getMap()方法的返回类型。

But if you use the polymorphism feature of Java, and instead of returning specific classes, use the interface Map , it improves code reusability and reduces the impact of requirement changes. 但是,如果您使用Java的多态性功能,而不是返回特定的类,请使用Map接口,它可以提高代码的可重用性并减少需求变更的影响。

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. 嗯,所以区别在于,Map通常具有与之关联的某些方法。 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. 但是创建地图的方式不同,例如HashMap,这些不同的方式提供了并非所有地图都具有的独特方法。

Exactly--and you always want to use the most general interface you possibly can. 的确如此-并且您始终希望使用可能的最通用的界面。 Consider ArrayList vs LinkedList. 考虑ArrayList与LinkedList。 Huge difference in how you use them, but if you use "List" you can switch between them readily. 使用它们的方式差异很大,但是如果使用“列表”,则可以在它们之间轻松切换。

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)填充它,但是该方法将返回不需要不再作为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". 请注意,让您的代码的一部分将其视为“ HashMap”是可以接受的,而另一部分则将其视为“ Map”是可以接受的,但是这应该“向下”进行。 so that you are never casting. 这样你就永远不会投降。

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则没有。

As noted by TJ Crowder and Adamski, one reference is to an interface, the other to a specific implementation of the interface. 正如TJ Crowder和Adamski所指出的,一种参考是接口,另一种是接口的特定实现。 According to Joshua Block, you should always attempt to code to interfaces, to allow you to better handle changes to underlying implementation - ie 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. 根据Joshua Block的说法,您应该始终尝试对接口进行编码,以使您能够更好地处理对基础实现的更改-即,如果HashMap突然对于您的解决方案不理想,并且您需要更改Map的实现,那么您仍然可以使用Map接口,并更改实例化类型。

In your second example the "map" reference is of type Map , which is an interface implemented by HashMap (and other types of Map ). 在第二个示例中,“ map”引用的类型为Map ,这是由HashMap (以及其他类型的Map )实现的接口。 This interface is a contract saying that the object maps keys to values and supports various operations (eg put , get ). 这个接口是一个约定,表示对象将键映射到值并支持各种操作(例如putget )。 It says nothing about the implementation of the Map (in this case a HashMap ). 没有提到 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定义将特定的Map实现公开给方法。

Map is the static type of map, while HashMap is the dynamic type of map. Map是地图的静态类型 ,而HashMap是地图的动态类型 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类型之一,即使在运行时它可能指向它的任何子类型。

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 (eg 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. 一个好的经验法则是在API级别上保持尽可能抽象:例如,如果您要编程的方法必须在地图上工作,则将参数声明为Map而不是更严格(因为不那么抽象)的HashMap类型就足够了。 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实现传递给您的方法。

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

Map是接口,Hashmap是实现Map Interface的类

Adding to the top voted answer and many ones above stressing the "more generic, better", I would like to dig a little bit more. 除了最受投票支持的答案之外,还有很多人都强调“更通用,更好”,我想再多谈一点。

Map is the structure contract while HashMap is an implementation providing its own methods to deal with different real problems: how to calculate index, what is the capacity and how to increment it, how to insert, how to keep the index unique, etc. Map是结构协定,而HashMap是提供自己的方法来解决不同实际问题的实现:如何计算索引,容量是多少,如何递增,如何插入,如何保持索引唯一等。

Let's look into the source code: 让我们看一下源代码:

In Map we have the method of containsKey(Object key) : Map我们有containsKey(Object key)

boolean containsKey(Object key);

JavaDoc: JavaDoc:

boolean java.util.Map.containsValue(Object value) boolean java.util.Map.containsValue(对象值)

Returns true if this map maps one or more keys to the specified value. 如果此映射将一个或多个键映射到指定值,则返回true。 More formally, returns true if and only if this map contains at least one mapping to a value v such that (value==null ? v==null : value.equals(v)) . 更正式地讲,当且仅当此映射包含至少一个到值v映射(value==null ? v==null : value.equals(v)) ,返回true。 This operation will probably require time linear in the map size for most implementations of the Map interface. 对于Map接口的大多数实现,此操作可能需要地图大小中的时间线性。

Parameters:value 参数:值

value whose presence in this map is to betested 其在此地图中的存在将被证明的值

Returns:true 返回值:true

if this map maps one or more keys to the specified 如果此映射将一个或多个键映射到指定的

valueThrows: valueThrows:

ClassCastException - if the value is of an inappropriate type for this map (optional) ClassCastException-如果此地图的值类型不合适(可选)

NullPointerException - if the specified value is null and this map does not permit null values (optional) NullPointerException-如果指定的值为null,并且此映射不允许使用null值(可选)

It requires its implementations to implement it, but the "how to" is at its freedom, only to ensure it returns correct. 它需要它的实现来实现,但是“如何做”是自由的,只是确保它返回正确。

In HashMap : HashMap

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

It turns out that HashMap uses hashcode to test if this map contains the key. 事实证明, HashMap使用哈希码来测试此映射是否包含密钥。 So it has the benefit of hash algorithm. 因此,它具有哈希算法的优势。

Map is the Interface and Hashmap is the class that implements that. Map是Interface,而Hashmap是实现该接口的类。

So in this implementation you create the same objects 因此,在此实现中,您将创建相同的对象

HashMap是Map的实现,因此完全相同,但是具有“ clone()”方法,如我在参考指南中看到的那样))

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. 首先, Map是它有不同的实现等的接口- HashMapTreeHashMapLinkedHashMap等接口就像为实现类超类。 So according to OOP's rule any concrete class that implements Map is a Map also. 因此,根据OOP的规则,实现Map任何具体类也是Map That means we can assign/put any HashMap type variable to a Map type variable without any type of casting. 这意味着我们可以将任何HashMap类型变量分配/放置到Map类型变量,而无需任何类型的转换。

In this case we can assign map1 to map2 without any casting or any losing of data - 在这种情况下,我们可以将map1分配给map2而不会进行任何强制转换或数据丢失-

map2 = map1

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM