2009-08-28 3 views
278

В чем разница между следующими картами, которые я создаю (другой вопрос, люди отвечали, используя их, казалось бы, взаимозаменяемые и мне интересно, если/как они отличаются):В чем разница между объектами HashMap и Map в Java?

HashMap<String, Object> map = new HashMap<String, Object>(); 
Map<String, Object> map = new HashMap<String, Object>(); 

ответ

329

Там нет никакой разницы между объектами; у вас есть 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->, который он делит (с помощью методов доступа) с подклассов. Скажем, я пишу его с HashMap s, чтобы начать с того, что я думаю, что это подходящая структура для использования при написании класса.

Позже Мэри пишет код, подклассифицирующий его. У нее есть то, что ей нужно сделать как с 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, но теперь он предоставляет TreeMaps. Таким образом, мы должны исправить SpecialFoo (и этот вид может пульсировать через кодовую базу).

Если я не был очень хороший повод для обмена, что моя реализация была использованием HashMap (и что это произойдет), что я должен был сделать было объявить getThings и getMoreThings просто как возвращение Map<String, Object>, не будучи более специфичным, чем это.На самом деле, за исключением хороший повод, чтобы сделать что-то еще, даже в Foo я, вероятно, следует объявить things и moreThings в 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 остановки компиляции.

Интерфейсы (и базовые классы) позволяют выявить только столько, сколько необходимо, сохраняя нашу гибкость под обложками, чтобы вносить соответствующие изменения. В общем, мы хотим, чтобы наши ссылки были максимально простыми. Если нам не нужно знать, что это HashMap, просто назовите его Map.

Это не слепое правило, но в целом кодирование на самый общий интерфейс будет менее хрупким, чем кодирование в нечто более конкретное. Если бы я вспомнил об этом, я бы не создал Foo, который установил Mary для отказа с SpecialFoo. Если Мэри помнил это, то, хотя я перепутал Foo, она объявила бы свой частный метод с Map вместо HashMap, и мой сменивший контракт Foo не повлиял бы на ее код.

Иногда вы не можете этого сделать, иногда вы должны быть конкретными. Но если у вас нет оснований быть, перепутайте с наименее определенным интерфейсом.

+0

так что единственное различие заключается в том, что когда я передаю его как параметр, то мне нужно ссылаться на него как на карту , а на другую как HashMap , но они действительно такие же точные типы объектов? – sepiroth

+0

Да, это тот же самый объект, речь идет о * контракте *, который вы формируете, используя любой код.Я немного уточнил ответ, чтобы уточнить. –

+44

ах, поэтому разница в том, что в целом у Карты есть определенные методы, связанные с ней. но существуют разные способы создания карты, такие как HashMap, и эти разные способы предоставляют уникальные методы, которые не все карты имеют. Поэтому, если я использую Map, я могу использовать только методы Map, но у меня есть HashMap, поэтому на карте видны любые преимущества скорости, преимущества поиска и т. Д. HashMap. И если бы я использовал HashMap, я мог бы использовать эти специальные методы HashMap, и если мне в конечном итоге нужно будет изменить тип карты, это намного больше. – sepiroth

3

создаются одинаковые карты.

Но вы можете заполнить разницу, когда будете использовать его. При первом случае вы будете иметь возможность использовать специальные методы HashMap (но я не помню никого действительно полезно), и вы будете в состоянии передать его в качестве параметра HashMap:

public void foo (HashMap<String, Object) { ... } 

... 

HashMap<String, Object> m1 = ...; 
Map<String, Object> m2 = ...; 

foo (m1); 
foo ((HashMap<String, Object>)m2); 
8

В вашем втором примере ссылка «карта» имеет тип Map, который является интерфейсом, реализованным HashMap (и другими типами Map). Этот интерфейс является контрактом, говорящим, что объект сопоставляет ключи со значениями и поддерживает различные операции (например, put, get). Он говорит ничего об осуществленииMap (в данном случае a HashMap).

Второй подход обычно предпочтительнее, поскольку вы, как правило, не хотели бы подвергать конкретную реализацию карты методам с использованием Map или через определение API.

1

Карта - это интерфейс, а Hashmap - это класс, который реализует это.

Таким образом, в этой реализации вы создаете те же объекты

12

Как отмечает TJ Краудера и Adamski, одна ссылка является на интерфейс, другой конкретной реализации интерфейса. Согласно Joshua Block, вы всегда должны пытаться кодировать интерфейсы, чтобы вы могли лучше обрабатывать изменения в базовой реализации - то есть, если HashMap внезапно не был идеальным для вашего решения, и вам нужно было изменить реализацию карты, вы все равно могли бы использовать Map интерфейс и изменить тип экземпляра.

51

Map - это интерфейс, который реализует HashMap.Разница в том, что во второй реализации ваша ссылка на HashMap позволит использовать функции, определенные в интерфейсе карты, в то время как первая позволит использовать любые общедоступные функции в HashMap (включая интерфейс карты).

Это, вероятно, больше смысла, если вы читаете Sun's interface tutorial

+0

Я предполагаю: first = HashMap map = new HashMap (); – OneWorld

+0

Это похоже на то, как часто List реализуется как ArrayList – Gerard

8

Карта является статическим типом карты, в то время как HashMap является динамическим типом карты. Это означает, что компилятор будет рассматривать ваш объект карты как один из типов Map, хотя во время выполнения он может указывать на любой подтип этого объекта.

Эта практика программирования против интерфейсов вместо реализаций имеет дополнительное преимущество гибкости: вы можете, например, заменить динамический тип карты во время выполнения, если это подтип карты (например, LinkedHashMap) и изменить поведение карты на лету.

Хорошее эмпирическое правило должно оставаться как можно более абстрактным на уровне API: если, например, метод, который вы программируете, должен работать на картах, тогда достаточно указать параметр как карту вместо строгой (потому что меньше аннотация) Тип HashMap. Таким образом, потребитель вашего API может быть гибким в отношении того, какую реализацию карты они хотят передать вашему методу.

17

я только собирался сделать это в качестве комментария на принятый ответ, но он получил слишком напуганный (я ненавижу, не имеющие разрывы строк)

ах, так что разница в том, что в общем, Карта имеет определенные методы , связанные с ним. но есть разными способами или создать карту, такую ​​как как HashMap, и этими разными способами предоставляют уникальные методы, которые не все имеют карты.

Точно - и вы всегда хотите использовать наиболее общий интерфейс, который вы можете использовать. Рассмотрим ArrayList и LinkedList. Огромная разница в том, как вы их используете, но если вы используете «Список», вы можете легко переключаться между ними.

На самом деле вы можете заменить правую часть инициализатора более динамическим выражением. как о чем-то вроде этого:

List collection; 
if(keepSorted) 
    collection=new LinkedList(); 
else 
    collection=new ArrayList(); 

Таким образом, если вы собираетесь заполнить в коллекции с вставной рода, вы бы использовать связанный список (вставка сортировки в списке массива преступно.) Но если вы не нужно сортировать и просто добавлять, вы используете ArrayList (более эффективен для других операций).

Это довольно большой участок здесь, потому что коллекции - не лучший пример, но в дизайне OO одним из наиболее важных понятий является использование интерфейса фасада для доступа к различным объектам с тем же кодом.

Редактировать в ответ на комментарий:

Как для отображения комментариев ниже, да с помощью «Карты» интерфейс ограничивает вас только те методы, если вы не отданной коллекции назад от карты к HashMap (который ПОЛНОСТЬЮ побеждает цель).

Часто вы должны создать объект и заполнить его с использованием его определенного типа (HashMap), в каком-либо методе «создать» или «инициализировать», но этот метод вернет «карту», t нужно больше манипулировать как HashMap.

Если вам когда-либо приходилось бросать, вы, вероятно, используете неправильный интерфейс, или ваш код недостаточно структурирован. Обратите внимание, что допустимо, чтобы один раздел вашего кода рассматривал его как «HashMap», а другой рассматривал его как «карту», ​​но это должно происходить «вниз». так что вы никогда не бросаете.

Также обратите внимание на полуострый вид ролей, обозначенный интерфейсами. LinkedList создает хороший стек или очередь, ArrayList создает хороший стек, но ужасная очередь (опять же, удаление приведет к смещению всего списка), поэтому LinkedList реализует интерфейс Queue, ArrayList этого не делает.

+0

, но в этом примере я получаю только методы из общего класса List, правильно? независимо от того, сделаю ли я LinkedList() или ArrayList()? это просто, если я использую сортировку вставки (который, как я полагаю, должен быть методом List, который LinkedList и ArrayList получают по наследству), он работает быстрее в LinkedList? – sepiroth

+0

Я догадываюсь, что я ищу, независимо от того, говорю я или нет. Map m = new HashMap () my Map m может использовать методы, специфичные для HashMap, или нет. Я думаю, что это невозможно? – sepiroth

+0

ah, wait, no, my Map m сверху должен иметь методы из HashMap. – sepiroth

0

HashMap является реализацией карты, так что это совершенно то же самое, но есть «клон()» метод, как я вижу, в справочном руководстве))

0
HashMap<String, Object> map1 = new HashMap<String, Object>(); 
Map<String, Object> map2 = new HashMap<String, Object>(); 

Прежде всего Map является интерфейс имеет различные реализации, как - HashMap, TreeHashMap, LinkedHashMap и т. Д. Интерфейс работает как суперкласс для класса реализации. Таким образом, согласно правилу ООП, любой конкретный класс, который реализует Map, также является Map. Это означает, что мы можем назначить/поместить любую переменную типа HashMap в переменную типа Map без какого-либо литья.

В этом случае мы можем назначить map1 для map2 без литья или любого проигрыша данных -

map2 = map1 
16

enter image description here

Карта имеющие следующие реализациях

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. Дерево Карта Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

Предположим, вы создали один метод (Это просто spudo код).

public void HashMap getMap(){ 
    return map; 
} 

Предположим, что вы проецировать требования меняются каждый раз, как следует,

  1. Метод должен возвращать содержимое карты - нужно вернуть HashMap.
  2. Метод должен вернуть ключ карты в порядке ввода - необходимо изменить тип возврата HashMap на LinkedHashMap.
  3. Метод должен возвращать карту ключей в отсортированном порядке - необходимо изменить тип возврата LinkedHashMap на TreeMap.

Если ваш метод, возвращающий конкретные классы вместо Map интерфейса вы должны изменить тип возвращаемого значения методы getMap() каждый раз.

Но, если вы используете полиморфизм особенность Java, вместо того чтобы вернуться конкретный класс, используемый интерфейс Map, это приводит повторное использование кода и меньшее влияние, если любое изменение требований.

2

Карта интерфейса и HashMap это класс, который реализует интерфейс карты

0

Добавление к вершине проголосовали ответ, и многие из них выше подчеркивая «более общий, лучше», я хотел бы копнуть немного больше.

Map является контракт структуры, в то время как HashMap представляет собой реализацию, предоставляющую свои собственные методы для решения различных реальных проблем: как рассчитать индекс, какова емкость и как его увеличить, как вставить, как сохранить индекс уникальным и т.д.

Давайте посмотрим на исходный код:

В Map мы имеем метод containsKey(Object key):

boolean containsKey(Object key); 

Java Doc:

логическое java.util.Map.containsValue (значение объекта)

Возвращает истину, если эта карта отображает один или несколько ключей к указанному значению. Более формально возвращает true тогда и только тогда, когда эта карта содержит хотя бы одно сопоставление со значением v таким образом, что (value==null ? v==null : value.equals(v)). Эта операция, вероятно, потребует времени, линейного по размеру карты для большинства реализаций интерфейса карты.

Параметры: значение

значение, чье присутствие в этой карте является betested

Возвращает: true

если эта карта отображает один или несколько ключей к указанным

valueThrows:

ClassCastException - если значение имеет несоответствующий тип для этой карты (необязательно)

NullPointerException - если указанное значение равно нулю, и эта карта не допускает нулевые значения (необязательно)

Он требует, чтобы его реализации, чтобы реализовать его, но «как» находится на свободе, только для того, он возвращается правильно.

В HashMap:

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

Оказывается, что HashMap использует хэш-код, чтобы проверить, если эта карта содержит ключ. Таким образом, он имеет преимущество алгоритма хеширования.

Смежные вопросы