Каково различие между объектами HashMap и Карты в Java?

Выезд:

В Вашем собственном плавании типа SQL Server случая карты к SQL Server CLR SqlDouble и затем Двойной в .NET

Обновление (май 2016) :

Обновленная версия документа MSDN: Отображающиеся Данные Параметра CRL

332
задан 030 4 January 2017 в 23:50
поделиться

8 ответов

Нет никакой разницы между объектами; у вас есть HashMap в обоих случаях. Есть разница в интерфейсе , который у вас есть для объекта. В первом случае это HashMap , а во втором - Map . Но базовый объект тот же.

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

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

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

Если бы я сделал это, то Мэри сделала бы это:

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 и мое изменение контракта Фу не повлияло бы на ее код.

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

408
ответ дан 23 November 2019 в 00:45
поделиться

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

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

8
ответ дан 23 November 2019 в 00:45
поделиться

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

12
ответ дан 23 November 2019 в 00:45
поделиться

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

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

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

...

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

foo (m1);
foo ((HashMap<String, Object>)m2); 
3
ответ дан 23 November 2019 в 00:45
поделиться

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

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

54
ответ дан 23 November 2019 в 00:45
поделиться

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

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

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

8
ответ дан 23 November 2019 в 00:45
поделиться

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

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

1
ответ дан 23 November 2019 в 00:45
поделиться

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

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

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

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

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

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

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

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

17
ответ дан 23 November 2019 в 00:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: