回答:
オブジェクト間に違いはありません。あなたは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
sを提供したと言っていましたが、今では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>
実際にオブジェクトを作成するときにのみ特定できるように、使用可能なすべての場所で使用していることに注意してください。
私がそれをしたなら、メアリーはこれをしたでしょう:
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
プライベートメソッドを宣言し、私の変更した契約がコードに影響を与えることはなかったでしょう。Map
HashMap
Foo
時にはそれができないこともあれば、具体的にする必要があることもあります。しかし、理由がない限り、最も具体性のないインターフェースに向かって誤りを犯してください。
MapはHashMapが実装するインターフェースです。違いは、2番目の実装では、HashMapへの参照はMapインターフェースで定義された関数の使用のみを許可するのに対し、最初の実装ではHashMap(Mapインターフェースを含む)の任意のパブリック関数の使用を許可することです。
Sunのインターフェースチュートリアルを読んだ方がいいでしょう。
マップには次の実装があります。
HashMap Map m = new HashMap();
LinkedHashMap Map m = new LinkedHashMap();
ツリーマップ Map m = new TreeMap();
WeakHashMap Map m = new WeakHashMap();
1つのメソッドを作成したとします(これは単なる擬似コードです)。
public void HashMap getMap(){
return map;
}
プロジェクトの要件が変更されたとします。
HashMap
ます。 HashMap
をに変更する必要がありますLinkedHashMap
。 LinkedHashMap
をに変更する必要がありますTreeMap
。 メソッドがMap
インターフェースを実装するものではなく特定のクラスを返す場合、getMap()
毎回メソッドの戻りタイプを変更する必要があります。
ただし、Javaのポリモーフィズム機能を使用し、特定のクラスを返す代わりにインターフェースを使用すると、Map
コードの再利用性が向上し、要件の変更による影響が軽減されます。
受け入れられた回答へのコメントとしてこれを行うだけでしたが、ファンキーになりすぎました(改行がないのは嫌です)
ああ、それで違いは、一般的に、マップはそれに関連付けられた特定のメソッドを持っているということです。しかし、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はそうしません。
マップは静的なタイプのマップですが、HashMapは動的なタイプのマップです。これは、実行時にマップオブジェクトがそのサブタイプを指す場合でも、コンパイラーがマップオブジェクトをマップタイプの1つとして扱うことを意味します。
実装ではなくインターフェースに対するプログラミングのこのプラクティスには、柔軟性を維持するという追加の利点があります。たとえば、マップのサブタイプ(LinkedHashMapなど)である限り、実行時にマップの動的タイプを置き換え、マップの動作を変更できます。はえ。
経験則として、APIレベルでできるだけ抽象的にすることをお勧めします。たとえば、プログラミングしているメソッドがマップで機能する必要がある場合、パラメーターをより厳密ではなく(マップが少ないため)HashMapタイプではなくMapとして宣言するだけで十分です。 。そうすることで、APIのコンシューマーは、メソッドに渡すMapの実装の種類に柔軟に対応できます。
上位投票の回答と、「より一般的でより良い」を強調する上記の多くの回答に加えて、もう少し掘り下げたいと思います。
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
ハッシュコードを使用して、このマップにキーが含まれているかどうかをテストしていることがわかります。したがって、ハッシュアルゴリズムの利点があります。
同じマップを作成します。
しかし、それを使用するときに違いを埋めることができます。最初のケースでは、特別なHashMapメソッドを使用できるようになります(しかし、本当に役に立つ人を覚えていません)。これをHashMapパラメーターとして渡すことができます。
public void foo (HashMap<String, Object) { ... }
...
HashMap<String, Object> m1 = ...;
Map<String, Object> m2 = ...;
foo (m1);
foo ((HashMap<String, Object>)m2);
Mapはインターフェースであり、HashmapはMapインターフェースを実装するクラスです
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
まず第一にMap
、それはのような異なる実装を持つインタフェースである- 、、HashMap
などのインタフェースが実装するクラスのスーパークラスのように動作します。実装があることOOPのルール任意の具象クラスに応じだから、あるも。つまり、どのようなタイプのキャストも行わずに、任意のタイプ変数をタイプ変数に割り当て/配置できます。TreeHashMap
LinkedHashMap
Map
Map
HashMap
Map
このケースでは、割り当てることができますmap1
にmap2
任意のキャスティングまたはデータの任意の失わず-
map2 = map1