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



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



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

13 ответов:

нет никакой разницы между объектами; у вас есть 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, которые он разделяет (через методы доступа) с подклассами. Допустим, я пишу его с 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 до TreeMap. Теперь,SpecialFoo больше не компилируется, потому что я нарушил контракт:Foo раньше говорили, что это обеспечено HashMaps, но теперь он обеспечивает 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 это настроило Мэри на неудачу с SpecialFoo. Если Мария вспомнил об этом, хотя я все испортил Foo, она бы объявила свой личный метод с Map вместо HashMap и мои изменения Fooконтракт не повлиял бы на ее код.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Также обратите внимание на полу-аккуратный аспект роли указанных интерфейсов. 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 до LinkedHashMap.
  3. метод должен возвращать ключ карты в отсортированном порядке-нужно изменить тип возврата LinkedHashMap до 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 это реализация, предоставляющая свои собственные методы для решения различных реальных проблем: как вычислить индекс, какова емкость и как ее увеличить, как вставить, как сохранить индекс уникальным и т. д.

давайте заглянем в исходный код:

In 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 (необязательно)

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

In HashMap:

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

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

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

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

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

HashMap-это реализация Map, поэтому она совершенно одинакова, но имеет метод " 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 до map2 без какого-либо кастинга или любой потеря данных -

map2 = map1

Comments

    Ничего не найдено.