JavaのHashMapオブジェクトとMapオブジェクトの違いは何ですか?


349

私が作成した次のマップの違いは何ですか(別の質問では、人々は一見互換性があるように使用して答えており、それらがどのように/どのように違うのか疑問に思っています):

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

HashMapを使用して実装し、MaryがMapを使用するとします。コンパイルされますか?
GilbertS

回答:


446

オブジェクト間に違いはありません。あなたは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、に変更HashMapTreeMapます。今、SpecialFoo私は契約を破ったので、もうコンパイルしません。FooそれはHashMapsを提供したと言っていましたが、今ではTreeMaps代わりに提供しています。したがって、SpecialFoo今修正する必要があります(この種のことがコードベースに波及する可能性があります)。

私の実装がを使用していることを共有する本当に正当な理由がない限りHashMap(そしてそれは実際に起こります)、私がやるべきだったのは宣言でgetThingsありgetMoreThingsMap<String, Object>それ以上具体的ではなく戻るだけでした。実際、何かをする正当な理由がなければ、Fooおそらく/ ではなくthingsmoreThingsとして宣言する必要があります。MapHashMapTreeMap

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>実際にオブジェクトを作成するときにのみ特定できるように、使用可能なすべての場所で使用していることに注意してください。

私がそれをしたなら、メアリーはこれをしたでしょう:

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メアリーがで失敗するように設定したを作成しなかったでしょうSpecialFooメアリーがそれを覚えていたら、私がめちゃくちゃになっても、代わりにFooプライベートメソッドを宣言し、私の変更した契約がコードに影響を与えることはなかったでしょう。MapHashMapFoo

時にはそれができないこともあれば、具体的にする必要があることもあります。しかし、理由がない限り、最も具体性のないインターフェースに向かって誤りを犯してください。


56

MapHashMapが実装するインターフェースです。違いは、2番目の実装では、HashMapへの参照はMapインターフェースで定義された関数の使用のみを許可するのに対し、最初の実装ではHashMap(Mapインターフェースを含む)の任意のパブリック関数の使用を許可することです。

Sunのインターフェースチュートリアルを読んだ方がいいでしょう。


私は仮定します:最初= HashMap <String、Object> map = new HashMap <String、Object>();
OneWorld、

これは、リストがArrayListとして実装される頻度に似ています
Gerard

26

ここに画像の説明を入力してください

マップには次の実装があります。

  1. HashMap Map m = new HashMap();

  2. LinkedHashMap Map m = new LinkedHashMap();

  3. ツリーマップ Map m = new TreeMap();

  4. WeakHashMap Map m = new WeakHashMap();

1つのメソッドを作成したとします(これは単なる擬似コードです)。

public void HashMap getMap(){
   return map;
}

プロジェクトの要件が変更されたとします。

  1. メソッドはマップコンテンツを返す必要があります-返す必要がありHashMapます。
  2. メソッドは、マップキーを挿入順に返す必要があります-戻り値の型HashMapをに変更する必要がありますLinkedHashMap
  3. メソッドはマップキーをソート順に返す必要があります-戻り値の型LinkedHashMapをに変更する必要がありますTreeMap

メソッドがMapインターフェースを実装するものではなく特定のクラスを返す場合、getMap()毎回メソッドの戻りタイプを変更する必要があります。

ただし、Javaのポリモーフィズム機能を使用し、特定のクラスを返す代わりにインターフェースを使用すると、Mapコードの再利用性が向上し、要件の変更による影響が軽減されます。


17

受け入れられた回答へのコメントとしてこれを行うだけでしたが、ファンキーになりすぎました(改行がないのは嫌です)

ああ、それで違いは、一般的に、マップはそれに関連付けられた特定のメソッドを持っているということです。しかし、HashMapなど、マップの作成方法にはさまざまな方法があり、これらの方法によって、すべてのマップにあるわけではない独自のメソッドが提供されます。

正確に-そして、あなたは常にあなたがおそらくできる最も一般的なインターフェースを使いたいです。ArrayListとLinkedListを検討してください。それらの使い方には大きな違いがありますが、「リスト」を使用すると、それらを簡単に切り替えることができます。

実際、初期化子の右側をより動的なステートメントに置き換えることができます。このようなものはどうですか:

List collection;
if(keepSorted)
    collection=new LinkedList();
else
    collection=new ArrayList();

この方法で、コレクションに挿入ソートを入力する場合は、リンクリストを使用します(配列リストへの挿入ソートは犯罪です)。ただし、ソートを維持する必要がなく、追加するだけの場合は、 ArrayListを使用します(他の操作ではより効率的です)。

コレクションは最良の例ではないため、これはかなり大きなストレッチですが、OO設計で最も重要な概念の1つは、インターフェースファサードを使用してまったく同じコードで異なるオブジェクトにアクセスすることです。

コメントへの返信を編集:

以下のマップコメントについては、「マップ」インターフェースを使用すると、コレクションをマップからHashMapにキャストし直さない限り、これらのメソッドのみに制限されます(これは目的を完全に無効にします)。

多くの場合、オブジェクトを作成し、それを特定のタイプ(HashMap)を使用して、ある種の「作成」または「初期化」メソッドで入力しますが、そのメソッドは必要のない「マップ」を返しますHashMapとして操作されます。

キャストする必要がある場合は、おそらく間違ったインターフェイスを使用しているか、コードが十分に構造化されていません。コードの1つのセクションを「HashMap」として扱い、他のセクションを「Map」として扱うことは許容されますが、これは「下」に流れるはずです。キャストしないようにしてください。

また、インターフェースによって示される役割の半端な側面にも注意してください。LinkedListは適切なスタックまたはキューを作成し、ArrayListは適切なスタックを作成しますが、恐ろしいキューを作成します(この場合も、削除するとリスト全体がシフトします)。LinkedListはQueueインターフェースを実装しますが、ArrayListはそうしません。


しかし、この例では、一般的なListクラスからのみメソッドを取得しますよね?LinkedList()にするかArrayList()にするかに関係なく?それは私が挿入ソート(LinkedListとArrayListが継承によって取得するListのメソッドでなければならないことを想像する)を使用する場合、それはLinkedListでより高速に動作するということですか?
トニースターク

私が探しているのは、Map <string、string> m = new HashMap <string、string>()と言ったときに、Map mがHashMapに固有のメソッドを使用できるかどうかです。それはできないと思いますか?
トニースターク

ああ、ちょっと待って、いや、上のマップmにはHashMapのメソッドが必要です。
トニースターク

したがって、基本的に「インターフェイスの意味」でMapを使用する唯一の特典は、マップを必要とするメソッドがある場合、どのタイプのマップもこのメソッドで機能することを保証することです。しかし、ハッシュマップを使用した場合、その方法はハッシュマップでのみ機能すると言っています。または、別の言い方をすると、私のメソッドはMapクラスで定義されたメソッドのみを使用しますが、Mapを拡張する他のクラスによって継承されます。
トニースターク

私はインターフェイスのものが存在しなかった場合、私はコンパイルおよび実行する前に、1を選択する必要があるだろう一方で、実行時まで欲しいリストIのタイプを決定する必要はありませんリスト手段を使ってどこ特典に加えて、あなたは、前述しました
トニー・スターク

12

TJ CrowderとAdamskiが述べたように、1つの参照はインターフェースへの参照であり、もう1つの参照はインターフェースの特定の実装への参照です。Joshua Blockによると、基盤となる実装への変更をより適切に処理できるように、常にインターフェイスへのコーディングを試みる必要があります。つまり、HashMapが突然ソリューションに理想的でなくなり、マップの実装を変更する必要がある場合でも、マップを使用できます。インターフェース、およびインスタンス化タイプを変更します。


8

2番目の例では、「マップ」参照のタイプはですMap。これはによって実装されるインターフェースですHashMap(および他のタイプのMap)。このインターフェイスは、オブジェクトがキーを値にマップし、さまざまな操作(など)をサポートすることを示すコントラクトです。それは言わないの実装については何の(この場合はAを)。putgetMapHashMap

MapAPI定義を使用するかAPI定義を介してメソッドに特定のマップ実装を公開したくないので、2番目のアプローチが一般的に推奨されます。


8

マップは静的なタイプのマップですが、HashMapは動的なタイプのマップです。これは、実行時にマップオブジェクトがそのサブタイプを指す場合でも、コンパイラーがマップオブジェクトをマップタイプの1つとして扱うことを意味します。

実装ではなくインターフェースに対するプログラミングのこのプラクティスには、柔軟性を維持するという追加の利点があります。たとえば、マップのサブタイプ(LinkedHashMapなど)である限り、実行時にマップの動的タイプを置き換え、マップの動作を変更できます。はえ。

経験則として、APIレベルでできるだけ抽象的にすることをお勧めします。たとえば、プログラミングしているメソッドがマップで機能する必要がある場合、パラメーターをより厳密ではなく(マップが少ないため)HashMapタイプではなくMapとして宣言するだけで十分です。 。そうすることで、APIのコンシューマーは、メソッドに渡すMapの実装の種類に柔軟に対応できます。


4

上位投票の回答と、「より一般的でより良い」を強調する上記の多くの回答に加えて、もう少し掘り下げたいと思います。

Mapは構造コントラクトでHashMapあり、実装はさまざまな実際の問題に対処する独自のメソッドを提供します。インデックスの計算方法、容量とその増分方法、挿入方法、インデックスを一意に保つ方法などです。

ソースコードを見てみましょう:

ではMap、次の方法がありcontainsKey(Object key)ます。

boolean containsKey(Object key);

JavaDoc:

boolean java.util.Map.containsValue(Object value)

このマップが1つ以上のキーを指定された値にマップする場合、trueを返します。より正式には、このマップにvなどの値へのマッピングが少なくとも1つ含まれている場合にのみtrueを返します(value==null ? v==null : value.equals(v))。この操作には、マップインターフェイスのほとんどの実装で、マップサイズに比例した時間が必要になる可能性があります。

パラメータ:値

このマップでの存在がテストされる値

戻り値:true

このマップが1つ以上のキーを指定されたものにマップする場合

valueThrows:

ClassCastException-値がこのマップに不適切なタイプである場合(オプション)

NullPointerException-指定された値がnullであり、このマップがnull値を許可しない場合(オプション)

それを実装するには実装が必要ですが、「ハウツー」は自由に行うことができ、正しい結果が返されることを確認するためだけです。

HashMap

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

HashMapハッシュコードを使用して、このマップにキーが含まれているかどうかをテストしていることがわかります。したがって、ハッシュアルゴリズムの利点があります。


3

同じマップを作成します。

しかし、それを使用するときに違いを埋めることができます。最初のケースでは、特別なHashMapメソッドを使用できるようになります(しかし、本当に役に立つ人を覚えていません)。これをHashMapパラメーターとして渡すことができます。

public void foo (HashMap<String, Object) { ... }

...

HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;

foo (m1);
foo ((HashMap<String, Object>)m2); 

3

Mapはインターフェースであり、HashmapはMapインターフェースを実装するクラスです


1

Mapはインターフェースであり、Hashmapはそれを実装するクラスです。

この実装では、同じオブジェクトを作成します


0

HashMapはMapの実装なので、まったく同じですが、リファレンスガイドにあるように "clone()"メソッドがあります))


0
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();  

まず第一にMap、それはのような異なる実装を持つインタフェースである- 、、HashMap などのインタフェースが実装するクラスのスーパークラスのように動作します。実装があることOOPのルール任意の具象クラスに応じだから、あるも。つまり、どのようなタイプのキャストも行わずに、任意のタイプ変数をタイプ変数に割り当て/配置できます。TreeHashMapLinkedHashMapMapMapHashMapMap

このケースでは、割り当てることができますmap1map2任意のキャスティングまたはデータの任意の失わず-

map2 = map1
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.