В чем разница между объектами 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, которые он разделяет (с помощью методов доступа) с подклассами. Допустим, я пишу это с 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
to TreeMap
. Теперь,SpecialFoo
больше не компилируется, потому что я нарушил контракт:Foo
говорил он предоставил HashMap
s, но теперь он обеспечивает 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-нет.
карта, имеющая следующие реализации,
HashMap
Map m = new HashMap();
LinkedHashMap
Map m = new LinkedHashMap();
Дерево
Map m = new TreeMap();
WeakHashMap
Map m = new WeakHashMap();
Предположим, вы создали один метод (это просто код spudo).
public void HashMap getMap(){
return map;
}
предположим, что требования проекта меняются каждый раз как следует,
- метод должен возвращать содержимое карты-необходимо вернуть
HashMap
. - метод должен возвращать ключ карты в порядке вставки-необходимо изменить тип возврата
HashMap
toLinkedHashMap
. - метод должен возвращать ключ карты в отсортированном порядке-необходимо изменить тип возврата
LinkedHashMap
toTreeMap
.
если ваш метод возвращает определенные классы вместо 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-это реализация карты, поэтому она такая же, но имеет метод "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