Почему Java Map не расширяет коллекцию?

Я был удивлен тем, что Map<?,?> не является Collection<?>.

Я думал, что это создаст много смысла, если бы оно было объявлено как таковое:

public interface Map<K,V> extends Collection<Map.Entry<K,V>>

В конце концов, Map<K,V> представляет собой набор Map.Entry<K,V>, не так ли?

Так есть ли веская причина, почему это не реализовано как таковое?


Благодаря Cletus для наиболее авторитетного ответа, но мне все еще интересно, почему, если вы уже можете просматривать Map<K,V> как Set<Map.Entries<K,V>> (через entrySet()), он не просто расширяет этот интерфейс.

Если a Map является Collection, то какие элементы? Единственный разумный ответ - "пары ключ-значение"

Точно, interface Map<K,V> extends Set<Map.Entry<K,V>> было бы здорово!

но это обеспечивает очень ограниченную (и не особенно полезную) абстракцию Map.

Но если это так, то почему entrySet, заданный интерфейсом? Это должно быть полезно как-то (и я думаю, что легко спорить об этой позиции!).

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

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

Ответ 1

Из Часто задаваемые вопросы API API коллекций Java:

Почему карта не расширяет коллекцию?

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

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

Можно было собрать коллекцию Карта, но это ставит вопрос: какие ключи? Там действительно нет удовлетворительный ответ и принуждение одного приводит к неестественному интерфейсу.

Карты можно просмотреть как Коллекции (из ключи, значения или пары), и этот факт отражается в трех "Сборниках" просматривать операции "на Картах (keySet, entrySet и значения). Пока это, в принципе, можно просмотреть список как индексы Map Map для элементов, у этого есть неприятное свойство, которое удаление элемента из списка изменяет ключ, связанный с каждым элемент перед удаленным элементом. Вот почему у нас нет вида карты операции над списками.

Обновление: Я думаю, что цитата отвечает на большинство вопросов. Стоит подчеркнуть, что часть коллекции не является особенно полезной абстракцией. Например:

Set<Map.Entry<String,String>>

позволит:

set.add(entry("hello", "world"));
set.add(entry("hello", "world 2");

(предполагая метод entry(), который создает экземпляр Map.Entry)

Map требуют уникальных ключей, чтобы это нарушило это. Или если вы наложите уникальные ключи на Set записей, это не действительно Set в общем смысле. Это a Set с дальнейшими ограничениями.

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

Стоит отметить, что HashSet фактически реализован как HashMap, а не наоборот. Это чисто детализация реализации, но тем не менее интересно.

Основной причиной существования entrySet() является упрощение обхода, поэтому вам не нужно перемещаться по клавишам, а затем выполнять поиск ключа. Не считайте это prima facie доказательством того, что Map должен быть Set записей (imho).

Ответ 2

Я думаю, почему это субъективно.

В С# я думаю, что Dictionary расширяет или, по крайней мере, реализует коллекцию:

public class Dictionary<TKey, TValue> : IDictionary<TKey, TValue>, 
    ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, 
    IDictionary, ICollection, IEnumerable, ISerializable, IDeserializationCallback

В Pharo Smalltak:

Collection subclass: #Set
Set subclass: #Dictionary

Но есть асимметрия с некоторыми методами. Например, collect: будет связывать (эквивалент записи), а do: принимать значения. Они предоставляют другой метод keysAndValuesDo: для итерации словаря по записи. Add: принимает ассоциацию, но remove: был "подавлен":

remove: anObject
self shouldNotImplement 

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

Что лучше субъективно.

Ответ 3

Пока вы получили несколько ответов, которые прямо касаются вашего вопроса, я думаю, что было бы полезно немного отступить и рассмотреть вопрос в более общем плане. То есть, чтобы не смотреть конкретно на то, как написана библиотека Java, и посмотрите, почему она написана именно так.

Проблема заключается в том, что наследование моделирует только один тип общности. Если вы выделите две вещи, которые кажутся "подобными коллекции", вы, вероятно, можете выбрать 8 или 10 вещей, которые у них есть общего. Если вы выберете другую пару "похожих на коллекцию" вещей, они также будут иметь 8 или 10 вещей, но они не будут теми же 8 или 10 вещами, что и первая пара.

Если вы посмотрите на дюжину или около того разных "похожих на коллекцию" вещей, практически у каждого из них, вероятно, будет что-то вроде 8 или 10 характеристик вместе с хотя бы одним другим, но если вы посмотрите на то, что через каждого из них вы практически ничего не оставили.

Это ситуация, когда наследование (особенно единственное наследование) просто плохо моделирует. Там нет чистой разделительной линии между тем, кто из них действительно является коллекцией, а какие нет - но если вы хотите определить значимый класс Collection, вы застряли, оставив некоторые из них. Если вы оставите только некоторые из них, ваш класс Collection сможет предоставить довольно разреженный интерфейс. Если вы оставите больше, вы сможете дать ему более богатый интерфейс.

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

Это все еще оставляет одну проблему: почти независимо от того, что вы не учитываете, и что вы вставляете, вам нужно будет провести жесткую линию между тем, какие классы и какие из них. Независимо от того, где вы рисуете эту линию, у вас будет четкое, довольно искусственное разделение между некоторыми вещами, которые очень похожи.

Ответ 4

Ответ cletus хороший, но я хочу добавить семантический подход. Объединить оба они не имеют смысла, подумайте о том, что вы добавляете пару ключ-значение через интерфейс коллекции, и ключ уже существует. Интерфейс Map позволяет использовать только одно значение, связанное с ключом. Но если вы автоматически удаляете существующую запись с тем же ключом, коллекция имеет после добавления тот же размер, что и раньше, - очень неожиданно для коллекции.

Ответ 5

Пакеты Java нарушены. Отсутствует интерфейс, то есть Relation. Следовательно, Map extends Relation extends Set. Отношения (также называемые мульти-картами) имеют уникальные пары имя-значение. Карты (ака "Функции" ) имеют уникальные имена (или ключи), которые, конечно, сопоставляются значениям. Последовательности расширяют Карты (где каждый ключ является целым числом > 0). Сумки (или несколько наборов) расширяют Карты (где каждая клавиша является элементом, и каждое значение представляет собой количество раз, когда элемент появляется в сумке).

Эта структура позволит пересечение, объединение и т.д. диапазона "коллекций". Следовательно, иерархия должна быть:

                                Set

                                 |

                              Relation

                                 |

                                Map

                                / \

                             Bag Sequence

Sun/Oracle/Java ppl - пожалуйста, сделайте это в следующий раз. Спасибо.

Ответ 6

Если вы посмотрите на соответствующую структуру данных, вы можете легко понять, почему Map не является частью Collection. В каждом Collection хранится одно значение, где в качестве Map хранится пара ключей и значений. Поэтому методы в интерфейсе Collection несовместимы для интерфейса Map. Например, в Collection имеем add(Object o). Какова будет такая реализация в Map. Нет смысла иметь такой метод в Map. Вместо этого мы имеем метод put(key,value) в Map.

Тот же аргумент используется для методов addAll(), remove() и removeAll(). Поэтому основная причина заключается в том, что данные хранятся в Map и Collection. Также, если вы помните интерфейс Collection реализованный интерфейс Iterable, то любой интерфейс с .iterator() должен возвращать итератор, который должен позволять нам перебирать значения, хранящиеся в Collection. Теперь, что бы такой метод возвращался для Map? Ключевой итератор или Итератор значений? Это также не имеет смысла.

Есть способы, по которым мы можем перебирать ключи и значения, хранящиеся в Map, и именно так оно является частью структуры Collection.

Ответ 7

Точно, interface Map<K,V> extends Set<Map.Entry<K,V>> было бы здорово!

Собственно, если это было implements Map<K,V>, Set<Map.Entry<K,V>>, то я склонен согласиться. Это кажется даже естественным. Но это не очень хорошо, не так ли? Скажем, мы имеем HashMap implements Map<K,V>, Set<Map.Entry<K,V>, LinkedHashMap implements Map<K,V>, Set<Map.Entry<K,V> и т.д.... это все хорошо, но если у вас есть entrySet(), никто не забудет реализовать этот метод, и вы можете быть уверены, что можете получить entrySet для любой Карты, в то время как вы не хотите, если вы надеетесь, что разработчик реализовал оба интерфейса...

Причина, по которой я не хочу иметь interface Map<K,V> extends Set<Map.Entry<K,V>>, просто потому, что будет больше методов. И в конце концов, это разные вещи, верно? Также очень практически, если я ударил map. в среде IDE, я не хочу видеть .remove(Object obj) и .remove(Map.Entry<K,V> entry), потому что я не могу сделать hit ctrl+space, r, return и делать с ним.

Ответ 8

Карта имеет три разных набора: набор , набор записей и набор пар ключ-значение.
Вы можете получить любой из трех с помощью одного вызова метода.

Только набор пар ключ-значение можно разумно назвать "сборкой", и это довольно неестественный.
Можно сказать, что карта - это коллекция (на самом деле, набор) Map.Entry, но это просто неловко и странно.
Это еще менее склонно идентифицировать набор с его набором значений или его ключ комплект. Поэтому, вместо того, чтобы вводить в заблуждение, они сделали все три легко и одинаково доступными.

Ответ 9

Map<K,V> не следует расширять Set<Map.Entry<K,V>>, поскольку:

  • Вы не можете добавлять разные Map.Entry с тем же ключом к тому же Map, но
  • Вы можете добавить разные Map.Entry с тем же ключом к тому же Set<Map.Entry>.

Ответ 10

Прямо и просто. Коллекция - это интерфейс, который ожидает только один объект, тогда как для карты требуется два.

Collection(Object o);
Map<Object,Object>