В чем разница между объектами HashMap и Map в Java?

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

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

13 ответов


нет никакой разницы между объектами; у вас есть HashMap<String, Object> в обоих случаях. Существует разница в интерфейс у вас есть объект. В первом случае, интерфейс HashMap<String, Object>, тогда как во втором это Map<String, Object>. Но основной объект тот же самый.

преимущества использования Map<String, Object> заключается в том, что вы можете изменить базовый объект на другой вид карты, не нарушая контракт с любым кодом, который его использует. Если вы объявите его as 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, которые он разделяет (с помощью методов доступа) с подклассами. Допустим, я пишу это с HashMaps Для начала, потому что я думаю, что это подходящая структура для использования при написании класса.

позже Мэри пишет подкласс кода. У нее есть что-то, что она должна сделать с обоими 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 to TreeMap. Теперь,SpecialFoo больше не компилируется, потому что я нарушил контракт:Foo говорил он предоставил HashMaps, но теперь он обеспечивает 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 остановка компиляции.

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

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

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


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

Это, вероятно, будет иметь больше смысла, если Вы читаете учебник по интерфейсу Sun


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

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

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

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

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

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

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

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

Что касается вашего комментария карты ниже, да, использование интерфейса" карта " ограничивает вас только теми методы, Если вы не отбрасываете коллекцию с карты на HashMap (что полностью противоречит цели).

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

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

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


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 to LinkedHashMap.
  3. метод должен возвращать ключ карты в отсортированном порядке-необходимо изменить тип возврата LinkedHashMap to TreeMap.

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

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


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


во втором примере ссылка "карта" имеет тип Map, который является интерфейсом, реализованным HashMap (и других видов Map). Этот интерфейс является контракт говоря, что объект отображает ключи к значениям и поддерживает различные операции (например,put, get). Тут написано ничего о реализации на Map (в этом случае a HashMap).

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


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

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

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


вы создаете те же карты.

но вы можете заполнить разницу, когда будете ее использовать. В первом случае вы сможете использовать специальные методы 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 мы есть метод containsKey(Object key):

boolean containsKey(Object key);

JavaDoc:

логическая java.утиль.Карта.containsValue(Object значение)

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

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

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

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

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

valueThrows:

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

NullPointerException-если указанное значение равно null и эта карта не разрешает значения null (необязательно)

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

на HashMap:

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

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


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

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


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


HashMap-это реализация карты, поэтому она такая же, но имеет метод "clone ()", как я вижу в справочном руководстве))


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

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

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

map2 = map1