В чем разница между следующими утверждениями в терминах HashMap? [Дубликат]

Я бы не использовал URLEncoder. Помимо неправильного имени (URLEncoder не имеет ничего общего с URL-адресами), неэффективен (вместо Builder он использует StringBuffer и делает несколько других вещей, которые медленны). Также слишком легко его испортить.

Вместо этого я использовал бы URIBuilder или Spring org.springframework.web.util.UriUtils.encodeQuery или Commons Apache HttpClient . Причина заключается в том, что вам нужно избегать имени параметров запроса (то есть ответ BalusC q) иначе, чем значение параметра.

Единственный недостаток вышеизложенного (что я обнаружил болезненно) заключается в том, что URL не является истинным подмножеством URI .

Пример кода:

import org.apache.http.client.utils.URIBuilder;

URIBuilder ub = new URIBuilder("http://example.com/query");
ub.addParameter("q", "random word £500 bank \$");
String url = ub.toString();

// Result: http://example.com/query?q=random+word+%C2%A3500+bank+%24

Поскольку я просто ссылаюсь на другие ответы, я отметил это как вики сообщества. Не стесняйтесь редактировать.

298
задан 030 5 January 2017 в 10:50
поделиться

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

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

358
ответ дан T.J. Crowder 20 August 2018 в 11:09
поделиться
  • 1
    поэтому единственное различие заключается в том, что когда я передаю его как параметр, например, мне нужно ссылаться на него как на Map & lt; blah & gt; а другой - как HashMap & lt; blah & gt; но они действительно являются одним и тем же типом объекта? – Tony Stark 28 August 2009 в 17:52
  • 2
    Да, это тот же самый объект, речь идет о contract , который вы формируете, используя любой код. Я немного уточнил ответ, чтобы уточнить. – T.J. Crowder 28 August 2009 в 17:54
  • 3
    – Tony Stark 28 August 2009 в 17:58
  • 4
    Я думаю, что он говорит, что даже если вы ссылаетесь на HashMap как на карту, реализация остается HashMap, и поэтому ничего не меняется о том, как работают методы. Если вы изменили реализацию интерфейса Map, свойства исполнения (например, скорость) действительно могут измениться. – ColinD 28 August 2009 в 19:26
  • 5
    Это отличный ответ. Это было бы лучше с примерами. – Xonatron 13 February 2012 в 21:13

В вашем втором примере ссылка «map» имеет тип Map, который является интерфейсом, реализованным HashMap (и другими типами Map). Этот интерфейс представляет собой контракт, в котором говорится, что объект сопоставляет ключи со значениями и поддерживает различные операции (например, put, get). Он ничего не говорит о реализации Map (в данном случае a HashMap).

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

8
ответ дан Adamski 20 August 2018 в 11:09
поделиться

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

12
ответ дан aperkins 20 August 2018 в 11:09
поделиться
16
ответ дан atish shimpi 20 August 2018 в 11:09
поделиться
17
ответ дан Bill K 20 August 2018 в 11:09
поделиться

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

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

1
ответ дан Diego Dias 20 August 2018 в 11:09
поделиться

HashMap - это реализация Map, так что он совершенно такой же, но имеет метод «clone ()», как я вижу в справочном руководстве))

0
ответ дан kolyaseg 20 August 2018 в 11:09
поделиться

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

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

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

8
ответ дан Matthias 20 August 2018 в 11:09
поделиться

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

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

52
ответ дан mishal153 20 August 2018 в 11:09
поделиться
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();  

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

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

map2 = map1
0
ответ дан Razib 20 August 2018 в 11:09
поделиться

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

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

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

...

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

foo (m1);
foo ((HashMap<String, Object>)m2); 
3
ответ дан Roman 20 August 2018 в 11:09
поделиться

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

1
ответ дан user 20 August 2018 в 11:09
поделиться

Добавляя к верхнему проголосовавшему ответу и многим выше, подчеркивая «более общий, лучший», я хотел бы копать немного больше.

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

Давайте посмотрите в исходный код:

В Map у нас есть метод containsKey(Object key):

boolean containsKey(Object key);

JavaDoc:

boolean java .util.Map.containsValue (значение объекта)

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

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

значение, наличие которого на этой карте должно быть указано

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

, если это отображение отображает одну или несколько клавиш в указанное значение

Throws:

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

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

Для ее реализации требуются ее реализации, [7] HashMap:

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

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

2
ответ дан WesternGun 20 August 2018 в 11:09
поделиться
Другие вопросы по тегам:

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