답변:
개체간에 차이가 없습니다. 당신은이 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-> object의 내부 맵이 있습니다. HashMap
클래스를 작성할 때 사용할 적절한 구조라고 생각하기 때문에 s로 작성한다고 가정 해 봅시다 .
나중에 Mary는 코드를 서브 클래 싱하는 코드를 작성합니다. 그녀는 그녀가 모두 할 필요가 뭔가가 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
되었지만 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>
실제 개체를 만들 때만 구체적으로 할 수있는 모든 곳에서 사용 하고 있습니다.
내가 그렇게했다면 Mary는 다음을 수행했을 것입니다.
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
Mary가 실패로 설정 한 것을 만들지 않았을 것 입니다 SpecialFoo
. Mary 가 그것을 기억 했다면 , 내가 엉망이더라도 Foo
, 그녀는 Map
대신에 그녀의 개인적인 방법을 선언했을 것이고 HashMap
나의 Foo
계약 변경 은 그녀의 코드에 영향을 미치지 않을 것입니다.
때로는 그렇게 할 수 없으며 때로는 구체적이어야합니다. 그러나 그럴만한 이유가 없다면, 가장 구체적인 인터페이스를 찾아보십시오.
Map 은 HashMap이 구현 하는 인터페이스입니다 . 차이점은 두 번째 구현에서 HashMap에 대한 참조는 Map 인터페이스에 정의 된 함수 만 사용할 수 있고 첫 번째는 HashMap의 모든 공용 함수 (Map 인터페이스 포함)를 사용할 수 있다는 것입니다.
Sun의 인터페이스 자습서 를 읽으면 더 이해가 될 것입니다
Map은 다음과 같은 구현을가집니다.
해시 맵 Map m = new HashMap();
LinkedHashMap Map m = new LinkedHashMap();
트리 맵 Map m = new TreeMap();
약한 해시지도 Map m = new WeakHashMap();
하나의 메소드를 작성했다고 가정하십시오 (이것은 단지 의사 코드 임).
public void HashMap getMap(){
return map;
}
프로젝트 요구 사항이 변경되었다고 가정하십시오.
HashMap
. 리턴해야합니다 . HashMap
합니다 LinkedHashMap
. 리턴 유형 을 로 변경해야 합니다 . LinkedHashMap
합니다 TreeMap
. 리턴 유형 을 로 변경해야 합니다 . 메소드가 Map
인터페이스 를 구현하는 대신 특정 클래스를 리턴하는 경우 getMap()
매번 메소드 의 리턴 유형을 변경해야합니다 .
그러나 Java의 다형성 기능을 사용하고 특정 클래스를 리턴하는 대신 interface를 사용 Map
하면 코드 재사용 성이 향상되고 요구 사항 변경의 영향이 줄어 듭니다.
나는 이것을 받아 들인 대답에 대한 의견으로 이것을하려고했지만 너무 펑키했습니다 (줄 바꿈이없는 것을 싫어합니다)
아, 차이점은 일반적으로 Map에는 이와 관련된 특정 방법이 있다는 것입니다. 그러나 HashMap과 같은 다른 방법이나 맵을 만드는 방법이 있으며 이러한 다른 방법은 모든 맵에없는 고유 한 방법을 제공합니다.
정확하고 항상 가능한 가장 일반적인 인터페이스를 사용하려고합니다. ArrayList와 LinkedList를 고려하십시오. 사용 방법이 크게 다르지만 "목록"을 사용하면 쉽게 전환 할 수 있습니다.
실제로 이니셜 라이저의 오른쪽을보다 동적 인 명령문으로 바꿀 수 있습니다. 이런 식으로 어떻습니까 :
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
이 방법으로 삽입 정렬을 사용하여 컬렉션을 채우려면 연결된 목록을 사용합니다 (배열 목록에 대한 삽입 정렬은 범죄입니다). 그러나 정렬 된 상태를 유지하고 추가 할 필요가없는 경우, ArrayList를 사용합니다 (다른 작업에 더 효율적).
컬렉션이 가장 좋은 예가 아니기 때문에 이것은 상당히 큰 확장입니다.하지만 OO 디자인에서 가장 중요한 개념 중 하나는 인터페이스 파사드를 사용하여 동일한 코드로 다른 객체에 액세스하는 것입니다.
댓글에 응답하여 수정 :
아래의 맵 주석과 관련하여, "Map"인터페이스를 사용하는 Yes는 콜렉션을 Map에서 HashMap으로 다시 캐스팅하지 않는 한 해당 메소드로만 제한합니다 (이는 목적을 완전히 상실 함).
종종 "만들기"또는 "초기화"방법으로 객체를 생성하고 특정 유형 (HashMap)을 사용하여 채우는 것이지만 그 방법은 필요하지 않은 "Map"을 반환합니다. 더 이상 HashMap으로 조작되었습니다.
방해가된다면 아마도 잘못된 인터페이스를 사용하고 있거나 코드가 제대로 구성되지 않았을 것입니다. 코드의 한 섹션이 "HashMap"으로 취급하고 다른 섹션은 "Map"으로 취급하도록하는 것이 허용되지만 "아래로"흐릅니다. 당신은 결코 캐스팅하지 않습니다.
또한 인터페이스가 나타내는 역할의 반 깔끔한 측면을 주목하십시오. LinkedList는 좋은 스택 또는 큐를 만들고, ArrayList는 좋은 스택을 만들지 만 끔찍한 큐 (다시 말하면 전체 목록이 바뀔 수 있음)이므로 LinkedList는 큐 인터페이스를 구현하지만 ArrayList는 그렇지 않습니다.
Map은 정적 유형 의지도이고 HashMap은 동적 유형 의지도입니다. 즉, 컴파일러는지도 객체를 런타임에지도의 하위 유형을 가리킬 수 있지만지도 유형 중 하나로 간주합니다.
구현 대신 인터페이스에 대한 프로그래밍에 대한 이러한 실습은 유연성을 유지하는 이점이 있습니다. 예를 들어 맵의 하위 유형 (예 : LinkedHashMap) 인 경우 런타임시 동적 유형의 맵을 교체하고 맵의 동작을 변경할 수 있습니다 파리.
경험적으로 가장 좋은 방법은 API 레벨에서 가능한 한 추상적으로 유지하는 것입니다. 예를 들어 프로그래밍하는 메소드가 맵에서 작동해야하는 경우 더 엄격한 (추상적이지 않기 때문에) HashMap 유형 대신 매개 변수를 Map으로 선언하면 충분합니다. . 그렇게하면 API 소비자는 어떤 종류의 맵 구현을 메소드에 전달할 것인지 유연하게 결정할 수 있습니다.
최상위 투표 답변과 "보다 일반적이고 더 나은"스트레스를 강조하는 위의 많은 답변에 덧붙여 조금 더 파고 싶습니다.
Map
구조 계약 HashMap
은 인덱스를 계산하는 방법, 용량 및 증가 방법, 삽입 방법, 인덱스를 고유하게 유지하는 방법 등 다양한 실제 문제를 처리하기위한 자체 메소드를 제공하는 구현입니다.
소스 코드를 살펴 보자.
에서 Map
우리의 방법을 가지고 containsKey(Object key)
:
boolean containsKey(Object key);
JavaDoc :
부울 java.util.Map.containsValue (객체 값)
이 맵이 하나 이상의 키를 지정된 값에 매핑하면 true를 반환합니다. 보다 공식적으로는,이 맵에 다음
v
과 같은 값에 대한 맵핑이 하나 이상 포함 된 경우에만 true를 리턴합니다(value==null ? v==null : value.equals(v))
. 이 작업은 대부분의 Map 인터페이스 구현에 대해 맵 크기에서 시간이 선형 일 것입니다.매개 변수 : value
이지도의 존재 여부를 테스트 할 값
리턴 : true
이 맵이 하나 이상의 키를 지정된 키에 매핑하는 경우
valueThrows :
ClassCastException-값이이 맵에 부적절한 형태 인 경우 (옵션)
NullPointerException-지정된 값이 null로,이 맵이 null 값을 허가하지 않는 경우 (옵션)
그것을 구현하기 위해서는 구현이 필요하지만, "방법"은 자유로울뿐 아니라 그것이 올바르게 리턴되도록 보장해야합니다.
에서 HashMap
:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
HashMap
이 맵에 키가 포함되어 있는지 테스트하기 위해 해시 코드를 사용 하는 것으로 나타났습니다 . 따라서 해시 알고리즘의 이점이 있습니다.
Map은 인터페이스이고 Hashmap은 Map Interface를 구현하는 클래스입니다.
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
우선은 Map
이 같은 다른 구현이있는 인터페이스입니다 - HashMap
, TreeHashMap
, LinkedHashMap
등 인터페이스 구현 클래스의 슈퍼 클래스처럼 작동합니다. 구현이 있음을 OOP의 규칙에 어떤 구체적인 클래스에 따라 그래서 Map
A는 Map
또한. 즉 HashMap
, Map
유형을 캐스팅하지 않고 유형 변수에 유형 변수를 할당 / 입력 할 수 있습니다 .
이 경우 우리는 할당 할 수 있습니다 map1
에 map2
있는 주조 또는 데이터의 손실없이 -
map2 = map1