Когда значение добавляется через Map.put (K, V), должен ли тот же экземпляр возвращаться через Map.get (K)? - PullRequest
5 голосов
/ 17 февраля 2011

Предположим, у вас есть этот код:

Map<Foo, Bar> map = new HashMap<Foo, Bar>();

Foo foo = new Foo();
Bar bar = new Bar();
map.put(foo, bar);

Bar barReturned = map.get(foo);

Требуется ли в Java barReturned == bar? То есть Java требует , чтобы barReturned был таким же экземпляром , что и bar? Если нет, какая семантика ожидается?

Javadoc предполагает, что barReturned == bar должно быть правдой, но я не уверен на 100%:

V get(Object key)

Возвращает значение, которому сопоставлен указанный ключ, или null, если эта карта не содержит сопоставления для ключа.

Более формально, если эта карта содержит отображение ключа k на значение v, такое, что (key==null ? k==null : key.equals(k)), тогда этот метод возвращает v; в противном случае возвращается null. (Может быть не более одного такого отображения.)

Если эта карта допускает значения null, то возвращаемое значение NULL не обязательно обязательно указывает, что карта не содержит сопоставления для ключа; также возможно, что карта явно отображает ключ на null. Операция containsKey может использоваться для различения этих двух случаев.

Параметры:

key - ключ, значение которого должно быть возвращено

Возвращает:

ЗНАЧЕНИЕ, НА КОТОРОЕ УКАЗАНО УКАЗАННЫЙ КЛЮЧ , или null, если эта карта не содержит сопоставления для ключа

(Акцент мой)

Редактировать: Я понимаю, что реализации Map, поставляемые в комплекте со стандартной библиотекой, придерживаются семантики barReturned == bar. То, что я хочу знать, является ли это обязательным согласно документации. Например, я должен также придерживаться этой семантики, если я напишу свой собственный класс, который реализует Map?

Ответы [ 7 ]

4 голосов
/ 17 февраля 2011

Если вы спрашиваете, можете ли вы разорвать это отношение, я думаю, что ответ «да».Например, если вы реализовали Map, которая действовала как постоянный кеш, конкретное значение может быть записано на диск, если оно не используется в течение некоторого времени, а затем снова загружено.у вас не будет эталонного равенства в этой ситуации, но это нормально.очевидно, вы хотели бы задокументировать любые отклонения от стандартного поведения, но я не думаю, что это выходит за рамки разумного использования карты.

1 голос
/ 18 марта 2011

Обратите внимание, что в нем указано «значение», которому сопоставлен ключ, а не «ссылка» или «экземпляр».Когда вы слышите «значение», он предлагает семантику Object.equals ().Вам не о чем беспокоиться.

1 голос
/ 17 февраля 2011

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

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

Patient: Doctor, it hurts when I do this
Doctor: Well, don't do that
1 голос
/ 17 февраля 2011

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

Map<Foo,Bar> map = new HashMap<Foo,Bar>();
Foo foo = new Foo();
Bar bar = new Bar();
map.put(foo, bar);

System.out.println( bar.equals( map.get(foo) ) ); // ==> Should be guaranteed
System.out.println( bar == map.get(foo) );        // ==> no guarantee

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

Мое мнение таково, что в вашей реализации вы должны гарантировать, что bar.equals( map.get(foo) ), но не обязательно bar == map.get(foo). Однако, пока вы четко документируете и утверждаете, что оба вышеперечисленных факта не гарантированы, вы можете делать то, что хотите, с помощью вашей реализации Map. ;)

1 голос
/ 17 февраля 2011

Если вы интерпретируете термин значение , используемый в javadocs, как ссылку на объект на карте, это будет означать, что вам всегда нужно возвращать ту же ссылку, что и вкарта и у вас будет == равенство.Тогда это становится больше вопросом определения термина «значение», используемого в документах.

1 голос
/ 17 февраля 2011

Конечно, это будет тот же экземпляр.В противном случае Map будет бесполезным.

Хотя единственным исключением из этого является многопоточное приложение, где другой поток может поместить произвольное значение в один и тот же ключ между вызовами put и get.


Редактировать Как отметил Адам в комментариях, у него есть особый случай, когда его реализация Map может воссоздавать входящие объекты и использовать вместо них их копии.Копии в смысле original.equals(copy) - это правда, а original == copy - это ложь.В этом случае, я думаю, карта может хранить копию вместо оригинала.

0 голосов
/ 17 февраля 2011
class Foo{

}
class Bar{

}
public class MapDemo {
    public static void main(String[] args) {
        Map<Foo,Bar> map = new HashMap<Foo,Bar>();
        Foo foo = new Foo();
        Bar bar = new Bar();
        map.put(foo, bar);
        System.out.println(bar == map.get(foo));
    }
}

Возвращает true

HashMap реализует Map но ничего такого не написано.

  /**
  137        * Returns the value of the mapping with the specified key.
  138        * 
  139        * @param key
  140        *            the key.
  141        * @return the value of the mapping with the specified key, or {@code null}
  142        *         if no mapping for the specified key is found.
  143        */

Но почти каждая реализация делает это, я имею в виду, что она поддерживает один и тот же объект

...