[java] Java에서 HashMap과 Map 객체의 차이점은 무엇입니까?

내가 만든 다음지도의 차이점은 무엇입니까?

HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();



답변

개체간에 차이가 없습니다. 당신은이 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는 코드를 서브 클래 싱하는 코드를 작성합니다. 그녀는 그녀가 모두 할 필요가 뭔가가 thingsmoreThings, 자연스럽게 그녀가 일반적인 방법으로 그 박았을, 그녀가 나에 사용 된 것과 동일한 유형을 사용 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.

이것은 맹목적인 규칙은 아니지만 일반적으로 가장 일반적인 인터페이스 로 코딩하는 것이 더 구체적인 것으로 코딩하는 것보다 덜 취약합니다. 내가 그것을 기억했다면, FooMary가 실패로 설정 한 것을 만들지 않았을 것 입니다 SpecialFoo. Mary 가 그것을 기억 했다면 , 내가 엉망이더라도 Foo, 그녀는 Map대신에 그녀의 개인적인 방법을 선언했을 것이고 HashMap나의 Foo계약 변경 은 그녀의 코드에 영향을 미치지 않을 것입니다.

때로는 그렇게 할 수 없으며 때로는 구체적이어야합니다. 그러나 그럴만한 이유가 없다면, 가장 구체적인 인터페이스를 찾아보십시오.


답변

MapHashMap이 구현 하는 인터페이스입니다 . 차이점은 두 번째 구현에서 HashMap에 대한 참조는 Map 인터페이스에 정의 된 함수 만 사용할 수 있고 첫 번째는 HashMap의 모든 공용 함수 (Map 인터페이스 포함)를 사용할 수 있다는 것입니다.

Sun의 인터페이스 자습서 를 읽으면 더 이해가 될 것입니다


답변

여기에 이미지 설명을 입력하십시오

Map은 다음과 같은 구현을가집니다.

  1. 해시 맵 Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. 트리 맵 Map m = new TreeMap();

  4. 약한 해시지도 Map m = new WeakHashMap();

하나의 메소드를 작성했다고 가정하십시오 (이것은 단지 의사 코드 임).

public void HashMap getMap(){
   return map;
}

프로젝트 요구 사항이 변경되었다고 가정하십시오.

  1. 이 메소드는 맵 컨텐츠를 리턴해야합니다 HashMap. 리턴해야합니다 .
  2. 메소드는 맵 키를 삽입 순서대로 리턴해야 HashMap합니다 LinkedHashMap. 리턴 유형 을 로 변경해야 합니다 .
  3. 메소드는 맵 키를 정렬 된 순서로 리턴해야 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는 그렇지 않습니다.


답변

TJ Crowder와 Adamski가 언급했듯이 하나는 인터페이스에 대한 것이고 다른 하나는 인터페이스의 특정 구현에 대한 것입니다. Joshua Block에 따르면 기본 구현에 대한 변경 사항을보다 잘 처리 할 수 ​​있도록 항상 인터페이스에 코드를 작성해야합니다. 즉, HashMap이 갑자기 솔루션에 적합하지 않고 맵 구현을 변경해야하는 경우에도 여전히 맵을 사용할 수 있습니다 인스턴스화 유형을 변경하십시오.


답변

두 번째 예에서 “map”참조는 유형 Map이며 이는 HashMap(및 다른 유형의 Map)에 의해 구현 된 인터페이스 입니다. 이 인터페이스는 것입니다 계약 오브젝트가 다양한 작업을 키를 값에 매핑하고 지원한다는 (예를 들어 put, get). (이 경우 a ) 구현대해서는 아무 것도 말하지 않습니다 .MapHashMap

두 번째 접근법은 일반적으로 MapAPI 정의를 사용 하거나 API 정의를 통해 특정 맵 구현을 메소드에 노출시키지 않으려는 경우에 일반적으로 선호됩니다 .


답변

Map은 정적 유형 의지도이고 HashMap은 동적 유형 의지도입니다. 즉, 컴파일러는지도 객체를 런타임에지도의 하위 유형을 가리킬 수 있지만지도 유형 중 하나로 간주합니다.

구현 대신 인터페이스에 대한 프로그래밍에 대한 이러한 실습은 유연성을 유지하는 이점이 있습니다. 예를 들어 맵의 하위 유형 (예 : LinkedHashMap) 인 경우 런타임시 동적 유형의 맵을 교체하고 맵의 동작을 변경할 수 있습니다 파리.

경험적으로 가장 좋은 방법은 API 레벨에서 가능한 한 추상적으로 유지하는 것입니다. 예를 들어 프로그래밍하는 메소드가 맵에서 작동해야하는 경우 더 엄격한 (추상적이지 않기 때문에) HashMap 유형 대신 매개 변수를 Map으로 선언하면 충분합니다. . 그렇게하면 API 소비자는 어떤 종류의 맵 구현을 메소드에 전달할 것인지 유연하게 결정할 수 있습니다.