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


146

나는 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지금은 중복됩니다 제외 )!


1
event Set <Map.Entry <K, V >> 실제로
— Maurice Perry

3
그렇지 않습니다. 논리적 결론이 아니라 의견 (디자인 FAQ에 설명되어 있음)을 기반으로합니다. 대안적인 접근 방법 은 Stepanov의 철저하고 화려한 분석을 기반으로 하는 C ++ STL ( sgi.com/tech/stl/table_of_contents.html ) 의 컨테이너 디자인을 살펴보십시오 .
— beldaz

답변:


123

로부터 자바 컬렉션 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) 이어야한다는 원시적 증거로 간주하지 마십시오 .


1
이 업데이트는 매우 설득력이 있지만 실제로는에서 반환 한 Set 뷰 entrySet()는 support를 지원 remove하지만 지원하지는 않습니다 add(아마도 throw UnsupportedException). 난 당신의 지점을 볼 수 있지만, 다시, 나는 OP의 지점을 참조 그래서, 너무 .. (내 자신의 의견을 내 자신의 대답에 명시된대로 ..)
— Enno시오 지

1
Zwei는 좋은 지적을 제시합니다. 뷰가 수행 add하는 방식과 같이 모든 합병증을 처리 할 수 ​​있습니다 entrySet(). 일부 작업은 허용하고 다른 작업은 허용하지 않습니다. 물론 자연스러운 반응은 "어떤 종류의 Set지원하지 add않습니까?"입니다. -그런 행동이 받아 들여질 수 있다면 entrySet()왜 받아 들여지지 this않습니까? 말했다, 나는 오전 에만 좋은의 API / OOP 디자인을 만드는 것 내 자신의 이해를 풍부하게하면 나는 생각되면,하지만 난 아직도, 그것은 더 논쟁의 가치 생각으로 대부분이 그런 좋은 생각이 아닌 이유의 이미 설득했다.
— polygenelubricants

3
FAQ에서 인용의 첫 번째 단락은 재미있다 : "우리는 느낀다 ... 따라서 거의 이해가되지 않는다" 나는 "느낌"과 "감각"이 결론을 형성한다고 생각하지 않는다. o)
— Peter Wippermann

지도를 확장하기 위해 컬렉션을 만들 수 있습니까? 왜 저자가 Collection확장을 생각 Map하는가?
— overexchange

11

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

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

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

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

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

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


10

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

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 

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

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


3

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


4
이것은 어떻게 Set작동하고, Set 않습니다 구현 Collection.
— Lii

2

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

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

                                Set

                                 |

                              Relation

                                 |

                                Map

                                / \

                             Bag Sequence

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


4
이 가상 인터페이스에 대한 API가 자세하게보고 싶습니다. 키가 정수 인 시퀀스 (일명 List)를 원하십니까? 엄청난 성능 저하처럼 들립니다.
— Lawrence Dol

맞습니다. 시퀀스가 ​​목록이므로 시퀀스가 ​​목록을 확장합니다. 이 정보에 액세스 할 수 있는지는 모르겠지만
— xagyg

@SoftwareMonkey는 다른 링크입니다. 마지막 링크는 API의 javadoc에 대한 링크입니다. zedlib.sourceforge.net
— xagyg

@SoftwareMonkey 나는 디자인이 나의 주요 관심사임을 부끄럽게 인정한다. Oracle / Sun의 전문가가이를 최적화 할 수 있다고 가정합니다.
— xagyg

나는 동의하지 않는 경향이있다. @cletus '답변의 예에서와 같이 도메인의 비 멤버 요소를 세트에 항상 추가 할 수없는 경우 "Map is-a Set"을 어떻게 유지할 수 있습니까?
— einpoklum

1

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


There are ways in which we can iterate over keys and values stores in a Map and that is how it is a part of Collection framework.이에 대한 코드 예제를 보여줄 수 있습니까?
— RamenChef

그것은 매우 잘 설명되었습니다. 이제 이해가된다. 감사!
— Emir Memic

구현은 객체 add(Object o)를 추가하는 Entry<ClassA, ClassB>것입니다. A Map는Collection of Tuples
— LIvanov

0

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


의미 적으로 "Map is-a Set on Map.Entry 's"라는 의미 론적으로 주장 할 수 있다면 관련 방법을 구현하는 데 방해가 될 것 같습니다. 만약 그 진술을 할 수 없다면, 그렇지 않을 것입니다 – 즉 귀찮게해야합니다.
— einpoklum

0

Map<K,V>Set<Map.Entry<K,V>>이후로 확장해서는 안됩니다 :

  • 같은 키를 가진 다른을 같은에 추가 할 수 는 없지만Map.EntryMap
  • 당신은 할 수 있습니다 다른 추가 Map.Entry같은 동일한 키로들 Set<Map.Entry>.

... 이것은 @cletus '답변의 두 번째 부분의 요점에 대한 간결한 진술입니다.
— einpoklum

-2

간단하고 간단합니다. Collection은 하나의 Object 만 필요한 인터페이스이지만 Map에는 2 개가 필요합니다.

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

좋은 Trinad, 당신의 대답은 항상 간단하고 짧습니다.
— Sairam
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.