В чем разница между следующими картами, которые я создаю (в другом вопросе люди ответили, используя их, казалось бы, взаимозаменяемо, и мне интересно, если / как они отличаются):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
java
dictionary
hashmap
Тони Старк
источник
источник
Ответы:
Там нет никакой разницы между объектами; у вас есть
HashMap<String, Object>
в обоих случаях. Существует разница в интерфейсе, который у вас есть к объекту. В первом случае интерфейс естьHashMap<String, Object>
, а во втором -Map<String, Object>
. Но основной объект тот же.Преимущество использования
Map<String, Object>
заключается в том, что вы можете изменить базовый объект на другой тип карты, не нарушая контракт с любым кодом, который его использует. Если вы объявляете это какHashMap<String, Object>
, вы должны изменить свой контракт, если хотите изменить базовую реализацию.Пример: допустим, я пишу этот класс:
У класса есть пара внутренних карт string-> object, которые он разделяет (через методы доступа) с подклассами. Допустим, я пишу это с
HashMap
s, чтобы начать, потому что я думаю, что это подходящая структура, чтобы использовать при написании класса.Позже Мэри пишет код, разделяющий его. У нее есть кое-что, что ей нужно сделать с обоими,
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
контракт на изменение не повлиял бы на ее код.Иногда ты не можешь этого сделать, иногда ты должен быть конкретным. Но если у вас нет причин быть ошибочными в сторону наименее конкретного интерфейса.
источник
Карта - это интерфейс, который реализует HashMap . Разница заключается в том, что во второй реализации ваша ссылка на HashMap будет позволять использовать только функции, определенные в интерфейсе Map, тогда как первая позволит использовать любые открытые функции в HashMap (который включает в себя интерфейс Map).
Это, вероятно, будет иметь больше смысла, если вы прочитаете руководство по интерфейсу Sun.
источник
Карта имеет следующие реализации:
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Древовидная карта
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Предположим, вы создали один метод (это просто псевдокод).
Предположим, что требования вашего проекта изменились:
HashMap
.HashMap
наLinkedHashMap
.LinkedHashMap
наTreeMap
.Если ваш метод возвращает конкретные классы вместо чего-то, что реализует
Map
интерфейс, вы должныgetMap()
каждый раз менять тип возвращаемого метода.Но если вы используете функцию полиморфизма Java и вместо того, чтобы возвращать определенные классы, используете интерфейс
Map
, это улучшает возможность повторного использования кода и уменьшает влияние изменений требований.источник
Я только собирался сделать это как комментарий к принятому ответу, но он стал слишком прикольным (я ненавижу не иметь разрывов строки)
Точно - и вы всегда хотите использовать самый общий интерфейс, какой только можете. Рассмотрим ArrayList против LinkedList. Огромная разница в том, как вы их используете, но если вы используете «Список», вы можете легко переключаться между ними.
Фактически, вы можете заменить правую часть инициализатора более динамичным оператором. как насчет чего-то вроде этого:
Таким образом, если вы собираетесь заполнить коллекцию сортировкой вставок, вы бы использовали связанный список (сортировка вставок в список массивов является преступной). Но если вам не нужно сохранять сортировку и просто добавляете, вы используете ArrayList (более эффективный для других операций).
Это довольно большая проблема, потому что коллекции - не лучший пример, но в ОО-дизайне одной из наиболее важных концепций является использование фасада интерфейса для доступа к различным объектам с одинаковым кодом.
Изменить ответ на комментарий:
Что касается вашего комментария к карте, приведенного ниже, то при использовании интерфейса «Карта» «Да» вы можете использовать только эти методы, если только вы не приведете коллекцию обратно из карты в HashMap (что ПОЛНОСТЬЮ не соответствует цели).
Часто вы будете создавать объект и заполнять его, используя определенный тип (HashMap), в каком-то методе «create» или «initialize», но этот метод будет возвращать «Map», который не нужно манипулировать как HashMap больше.
Кстати, если вам когда-либо придется выполнять приведение типов, возможно, вы используете неправильный интерфейс или ваш код недостаточно структурирован. Обратите внимание, что допустимо, чтобы один раздел вашего кода рассматривал его как «HashMap», в то время как другой обрабатывает его как «Map», но это должно течь «вниз». так что вы никогда не будете кастом.
Также обратите внимание на полунекативный аспект ролей, указанных интерфейсами. LinkedList создает хороший стек или очередь, ArrayList создает хороший стек, но ужасную очередь (опять же, удаление приведет к смещению всего списка), поэтому LinkedList реализует интерфейс Queue, а ArrayList - нет.
источник
Как отметили TJ Crowder и Adamski, одна ссылка относится к интерфейсу, другая - к конкретной реализации интерфейса. По словам Джошуа Блока, вы всегда должны пытаться кодировать интерфейсы, чтобы позволить вам лучше обрабатывать изменения в базовой реализации - т. Е. Если HashMap внезапно оказался не идеальным для вашего решения и вам нужно было изменить реализацию карты, вы все равно можете использовать карту интерфейс, и измените тип экземпляра.
источник
Во втором примере ссылка «карта» имеет тип
Map
, который является интерфейсом, реализованнымHashMap
(и другими типамиMap
). Этот интерфейс является контрактом, в котором говорится, что объект сопоставляет ключи со значениями и поддерживает различные операции (напримерput
,get
). Он не говорит ничего о реализации изMap
(в данном случаеHashMap
).Второй подход, как правило, предпочтительнее, так как обычно вы не хотите раскрывать конкретную реализацию карты методам, использующим
Map
или через определение API.источник
Карта - это статический тип карты, а HashMap - динамический тип карты. Это означает, что компилятор будет обрабатывать ваш объект карты как объект типа Map, хотя во время выполнения он может указывать на любой его подтип.
Эта практика программирования на основе интерфейсов вместо реализаций имеет дополнительное преимущество, заключающееся в том, что она остается гибкой: например, вы можете заменить динамический тип карты во время выполнения, если это подтип карты (например, LinkedHashMap), и изменить поведение карты в муха.
Хорошее практическое правило - оставаться как можно более абстрактным на уровне API: если, например, метод, который вы программируете, должен работать на картах, то достаточно объявить параметр как Map вместо более строгого (потому что менее абстрактного) типа HashMap , Таким образом, потребитель вашего API может быть гибким в отношении того, какую реализацию Map он хочет передать вашему методу.
источник
Добавляя к наиболее популярному ответу и многим из вышеперечисленных подчеркивая «более общий, лучший», я хотел бы еще немного покопаться.
Map
является структурным контрактом, в то время какHashMap
является реализацией, предоставляющей свои собственные методы для решения различных реальных проблем: как рассчитать индекс, какова емкость и как его увеличить, как вставить, как сохранить индекс уникальным и т. д.Давайте посмотрим на исходный код:
У
Map
нас есть методcontainsKey(Object key)
:JavaDoc:
Для его реализации требуются свои реализации, но «как это сделать» находится на его свободе, только для того, чтобы гарантировать, что он вернётся правильно.
В
HashMap
:Оказывается, что
HashMap
использует хеш-код для проверки, если эта карта содержит ключ. Таким образом, он имеет преимущество алгоритма хеширования.источник
Вы создаете те же карты.
Но вы можете заполнить разницу, когда вы будете использовать его. В первом случае вы сможете использовать специальные методы HashMap (но я не помню ни одного действительно полезного), и вы сможете передать его в качестве параметра HashMap:
источник
Карта - это интерфейс, а Hashmap - это класс, реализующий интерфейс карты.
источник
Map - это интерфейс, а Hashmap - это класс, который реализует это.
Таким образом, в этой реализации вы создаете те же объекты
источник
HashMap - это реализация Map, поэтому она почти такая же, но имеет метод "clone ()", как я вижу в справочном руководстве))
источник
Прежде всего
Map
, это интерфейс, который имеет различную реализацию, такую как -HashMap
,TreeHashMap
иLinkedHashMap
т. Д. Интерфейс работает как суперкласс для реализующего класса. Таким образом, согласно правилу ООП любой конкретный класс, который реализуетMap
этоMap
тоже. Это означает, что мы можем назначить / поместить любуюHashMap
переменнуюMap
типа в переменную типа без какого-либо типа приведения.В этом случае мы можем назначить ,
map1
чтобыmap2
без какого - либо литья или любого проигрыша данных -источник