[java] Java Map이 Collection을 확장하지 않는 이유는 무엇입니까?

나는 Map<?,?>아닌 사실에 놀랐습니다 Collection<?>.

나는 그것이 그렇게 선언되면 많은 의미가 있다고 생각했다.

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

결국,의 Map<K,V>모음입니다 Map.Entry<K,V>, 그렇지 않습니까?

그렇다면 왜 그렇게 구현되지 않은 좋은 이유가 있습니까?


가장 권위있는 답변 클리 터스 덕분에,하지만, 난 여전히 왜, 당신은 이미를 볼 수 있는지 궁금하네요 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지금은 중복됩니다 제외 )!



답변

로부터 자바 컬렉션 API 디자인 자주 묻는 질문 :

지도가 컬렉션을 확장하지 않는 이유는 무엇입니까?

이것은 의도적으로 설계된 것입니다. 우리는 매핑이 컬렉션이 아니며 컬렉션이 매핑이 아니라고 생각합니다. 따라서 Map이 Collection 인터페이스를 확장하는 것은 의미가 없습니다.

지도가 컬렉션 인 경우 요소는 무엇입니까? “키-값 쌍”이 합리적으로 정해지지 만 매우 제한적인 (특히 유용하지는 않은) 맵 추상화를 제공합니다. 주어진 키가 어떤 값에 매핑되는지 물어볼 수 없으며, 어떤 키에 매핑되는지를 몰라도 주어진 키에 대한 항목을 삭제할 수 없습니다.

Map을 확장하기 위해 컬렉션을 만들 수 있지만, 이것이 무엇입니까? 실제로 만족스러운 답변은 없으며 강제로 부 자연스러운 인터페이스를 만듭니다.

지도는 컬렉션 (키, 값 또는 쌍)으로 볼 수 있으며이 사실은지도의 세 가지 “컬렉션보기 작업”(keySet, entrySet 및 값)에 반영됩니다. 원칙적으로 List를 요소에 대한 맵 매핑 인덱스로 볼 수는 있지만 List에서 요소를 삭제하면 삭제 된 요소 이전의 모든 요소와 관련된 키가 변경되는 불쾌한 속성이 있습니다. 그렇기 때문에 목록에 대한지도보기 작업이 없습니다.

업데이트 : 견적이 대부분의 질문에 대한 답변이라고 생각합니다. 특별히 유용한 추상화가 아닌 항목 모음에 대해 강조 할 가치가 있습니다. 예를 들면 다음과 같습니다.

Set<Map.Entry<String,String>>

허용 할:

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

( 인스턴스 entry()를 생성 하는 메소드를 가정 Map.Entry)

Map고유 한 키가 필요하므로이를 위반할 수 있습니다. 또는 Set여러 항목 에 고유 키를 적용하면 실제로 Set일반적인 의미는 아닙니다. 그것은이다 Set더 제한.

논란의 여지가 있지만 equals()/ hashCode()관계 Map.Entry는 순전히 핵심이지만 문제가 있습니다. 더 중요한 것은 실제로 가치를 더 하는가? 코너 케이스를 살펴보기 시작하면이 추상화가 손상 될 수 있습니다.

HashSet는 실제로 HashMap다른 방법으로 구현 되지 않고 로 구현 된다는 점에 주목할 가치가 있습니다. 이것은 순전히 구현 세부 사항이지만 그럼에도 불구하고 흥미 롭습니다.

entrySet()존재 하는 주된 이유 는 순회를 단순화하여 키를 순회 한 다음 키를 조회 할 필요가 없기 때문입니다. Mapa Set가 항목 (imho) 이어야한다는 원시적 증거로 간주하지 마십시오 .


답변

귀하의 질문을 상당히 직접적으로 다루는 많은 답변을 얻었지만 조금 뒤로 물러나서 더 일반적으로 질문을 보는 것이 도움이 될 것입니다. 즉, Java 라이브러리가 어떻게 작성되는지 구체적으로 보지 말고 왜 그렇게 작성했는지 살펴보십시오.

여기서 문제는 상속은 한 가지 유형의 공통성을 모델링한다는 것 입니다. 둘 다 “컬렉션과 같은”것으로 보이는 두 가지를 골라낸다면 아마도 공통점이있는 8 개 또는 10 개를 선택할 수 있습니다. 다른 “컬렉션과 같은”항목을 선택하면 공통적으로 8 개 또는 10 개의 항목이 있지만 첫 번째 항목과 같은 8 개 또는 10 개 는 아닙니다 .

당신이 다스 또는 그렇게 다를 경우 “컬렉션 같은”것들, 거의 그들 모두는 아마 적어도 하나의 다른 하나의 공통점이 8 개 또는 10 특성 같은 것을해야합니다 -하지만 당신이 보면에서 공유 있는지 마다 하나 그들 중 거의 아무것도 남지 않았습니다.

이것은 상속 (특히 단일 상속)이 잘 모델링되지 않는 상황입니다. 어떤 컬렉션이 실제로 컬렉션인지 아닌지에 대한 명확한 구분선은 없지만 의미있는 Collection 클래스를 정의하려는 경우 일부를 생략해야합니다. 그중 몇 개만 남겨두면 Collection 클래스는 아주 드문 인터페이스 만 제공 할 수 있습니다. 더 많이 나가면 더 풍부한 인터페이스를 제공 할 수 있습니다.

일부는 또한 기본적으로 다음과 같은 옵션을 취합니다. “이 유형의 컬렉션은 작업 X를 지원하지만 X를 정의하는 기본 클래스에서 파생하여 사용할 수는 없지만 파생 된 클래스 X를 사용하려는 시도는 실패합니다 (예 : 예외를 던져서).

그것은 여전히 ​​하나의 문제를 남깁니다. 거의 어느 것이 당신이 떠나고 어느 것을 넣었 든 관계없이, 당신은 어떤 수업에 있는지와 어떤 수업에 있는지에 대한 단단한 선을 그려야합니다. 당신이 그 선을 어디에서 그리든지, 당신은 유사한 것들 사이에 분명하고 인공적인 구분을 남길 것입니다 .


답변

나는 그 이유를 추측 주관적인 .

C #에서는 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 

따라서 결정적으로 가능하지만 클래스 계층 구조와 관련된 다른 문제가 발생합니다.

더 나은 것은 주관적입니다.


답변

cletus의 대답은 좋지만 의미 론적 접근법을 추가하고 싶습니다. 두 가지를 결합하는 것은 의미가 없으며 수집 인터페이스를 통해 키-값 쌍을 추가하고 키가 이미 존재하는 경우를 생각해보십시오. 맵 인터페이스는 키와 관련된 하나의 값만 허용합니다. 그러나 동일한 키를 사용하여 기존 항목을 자동으로 제거하면 컬렉션은 이전과 동일한 크기를 추가 한 후에 컬렉션에 대해 예상치 못한 결과를 가져옵니다.


답변

Java 콜렉션이 손상되었습니다. 인터페이스의 인터페이스가 누락되었습니다. 따라서 맵은 관계 확장 세트를 확장합니다. 관계 (멀티 맵이라고도 함)에는 고유 한 이름-값 쌍이 있습니다. 맵 (일명 “함수”)에는 고유 한 이름 (또는 키)이 있으며 물론 값에 매핑됩니다. 시퀀스는 맵을 확장합니다 (각 키는 정수> 0). 가방 (또는 다중 세트)은 맵을 확장합니다 (여기서 각 키는 요소이고 각 값은 요소가 가방에 나타나는 횟수).

이 구조는 다양한 “컬렉션”의 교차점, 결합 등을 허용합니다. 따라서 계층 구조는 다음과 같아야합니다.

                                Set

                                 |

                              Relation

                                 |

                                Map

                                / \

                             Bag Sequence

Sun / Oracle / Java ppl-다음에 바로 받아보십시오. 감사.


답변

각 데이터 구조를 보면 왜 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.


답변

정확히, 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입니다.