私が作成した次のマップの違いは何ですか(別の質問では、人々は一見同じようにそれらを使用して答えました、そして、それらがどう違うか/どう違うか疑問に思います):
HashMap<String, Object> map = new HashMap<String, Object>();
Map<String, Object> map = new HashMap<String, Object>();
オブジェクト間に違いはありません。どちらの場合もHashMap<String, Object>
があります。 interfaceにはオブジェクトに違いがあります。前者の場合、インターフェースは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
で失敗するようにMaryを設定するSpecialFoo
を作成しなかったでしょう。 Maryを覚えていれば、Foo
を台無しにしたとしても、彼女はMap
の代わりにHashMap
を使用してプライベートメソッドを宣言し、Foo
の契約を変更してもコードには影響しませんでした。
それができない場合もあれば、具体的に説明する必要がある場合もあります。しかし、あなたがそうする理由がない限り、最も特定性の低いインターフェースに向かって誤ります。
Map は HashMap が実装するインターフェースです。違いは、2つ目の実装ではHashMapへの参照はMapインターフェースで定義された関数の使用のみを許可するのに対し、1つ目はHashMap(Mapインターフェースを含む)での任意のパブリック関数の使用を許可するということです。
Sunのインタフェースチュートリアル を読むと、おそらくもっと意味があるでしょう。
Mapには以下の実装があります。
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
を使用すると、コードの再利用性が向上し、要件変更の影響が少なくなります。
私はただ受け入れられた答えのコメントとしてこれをやろうとしていました、しかしそれはあまりにもファンキーになりました(改行を持っていないのは嫌いです)
ああ、違いは、一般的には、Mapはそれに関連付けられている特定のメソッドを持っているということです。しかし、HashMapなど、さまざまな方法やマップの作成方法があり、これらのさまざまな方法で、すべてのマップが持っているわけではない独自の方法が提供されます。
正確に - そして、あなたはいつも可能な限り最も一般的なインターフェースを使いたいのです。 ArrayListとLinkedListを比較してください。使い方は大きく異なりますが、「リスト」を使えば、すぐに切り替えることができます。
実際、イニシャライザの右側をより動的な文に置き換えることができます。このようなものはどうですか。
List collection;
if(keepSorted)
collection=new LinkedList();
else
collection=new ArrayList();
このように、コレクションを挿入ソートで埋める場合は、リンクリストを使用します(配列リストへの挿入ソートは犯罪です)。ただし、ソートする必要がなく、単に追加するだけの場合は、あなたはArrayListを使う(他の操作のためにより効率的)。
コレクションは最良の例ではないので、これはここではかなり大きなストレッチですが、OO designで最も重要な概念の1つは、まったく同じコードでさまざまなオブジェクトにアクセスするためのインタフェースファサードの使用です。
コメントに返信して編集:
下記のあなたの地図のコメントに関しては、あなたがMapからHashMap(目的を完全に無効にする)にコレクションをキャストしない限り、 "Map"インターフェースを使うYesはそれらのメソッドだけにあなたを制限します。
多くの場合、オブジェクトを作成し、特定のタイプ(HashMap)を使用して入力することです。ある種の "create"または "initialize"メソッドでは、そのメソッドは "Map"を返します。これ以上HashMapとして扱われます。
ところで、キャストする必要がある場合は、おそらく間違ったインターフェースを使用しているか、コードが十分に構造化されていません。コードの1つのセクションでそれを "HashMap"として扱い、もう1つのセクションでそれを "Map"として扱うことは許容されますが、これは "下へ"流れるはずです。あなたがキャストしないように。
また、インタフェースによって示される役割の半きれいな側面にも注意してください。 LinkedListは優れたスタックまたはキューを作成し、ArrayListは優れたスタックを作成しますが、恐ろしいキュー(削除するとリスト全体が移動します)なので、LinkedListはQueueインターフェイスを実装しますが、ArrayListは実装しません。
TJ CrowderとAdamskiによって言及されているように、1つの参照はインターフェースへの参照であり、もう1つはインターフェースの特定の実装への参照です。 Joshua Blockによると、基礎となる実装への変更をより適切に処理できるように、常にインターフェイスへのコーディングを試みる必要があります。インスタンス化タイプを変更します。
2番目の例では、 "map"参照はMap
型であり、これはHashMap
(およびその他のタイプのMap
)によって実装されるインタフェースです。このインタフェースは契約で、オブジェクトはキーを値にマッピングし、さまざまな操作(例:put
、get
)をサポートします。 Map
の実装については何もしません(この場合はHashMap
)。
通常、Map
を使用したりAPI定義を介して特定のマップ実装をメソッドに公開したくない場合、2番目の方法が一般的に推奨されます。
Mapはmapの静的型ですが、HashMapは動的型です。地図の。これは、実行時には、そのサブオブジェクトを指す可能性がある場合でも、コンパイラはマップオブジェクトをMap型のオブジェクトとして扱うことを意味します。
実装ではなくインタフェースに対してプログラミングを行うことで、柔軟性が維持されるという利点があります。たとえば、マップのサブタイプ(LinkedHashMapなど)であれば、動的タイプのマップを実行時に置き換えて、マップの動作を変更できます。その場.
たとえば、プログラミング中のメソッドがマップで機能する必要がある場合は、パラメータを厳密ではなくMapとして宣言するだけで十分です(抽象度が低いため)。HashMap型。そのようにして、あなたのAPIの消費者は、彼らがあなたのメソッドにどのようなMap実装を渡したいかについて柔軟になることができます。
同じマップを作成します。
しかし、あなたはそれを使うつもりでその違いを埋めることができます。最初のケースでは、特別な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
にはcontainsKey(Object key)
のメソッドがあります。
boolean containsKey(Object key);
JavaDoc:
boolean Java.util.Map.containsValue(Object value)
このマップが1つ以上のキーを指定された値にマッピングしている場合にtrueを返します。より正式には、このマップが
(value==null ? v==null : value.equals(v))
のような値v
へのマッピングを少なくとも1つ含む場合に限り、trueを返します。この操作では、Mapインタフェースのほとんどの実装で、おそらくマップサイズに線形の時間が必要になります。パラメータ:値
このマップ内の存在を確認する値
戻り値:true
このマップが1つ以上のキーを指定されたキーにマッピングするかどうか
valueThrows:
ClassCastException - 値がこのマップには不適切なタイプの場合(オプション)
NullPointerException - 指定された値がnullで、このマップがnull値を許可しない場合(オプション)
それはそれを実装するためにその実装を必要とします、しかしそれを正しく返すことを確実にするためだけに「方法」は自由にあります。
HashMap
内:
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
このマップにキーが含まれているかどうかをテストするためにHashMap
がハッシュコードを使用していることがわかります。そのため、ハッシュアルゴリズムの利点があります。
Mapはインターフェースであり、HashmapはMapインターフェースを実装するクラスです。
Mapはインターフェースで、Hashmapはそれを実装するクラスです。
したがって、この実装では同じオブジェクトを作成します。
HashMapはMapの実装なので、まったく同じですが、 "clone()"メソッドを持っています。参照ガイドを参照してください)
HashMap<String, Object> map1 = new HashMap<String, Object>();
Map<String, Object> map2 = new HashMap<String, Object>();
まず第一にMap
は - HashMap
、TreeHashMap
、LinkedHashMap
などのように異なる実装を持つインターフェースです。インターフェースは実装クラスのスーパークラスのように機能します。そのため、OOPの規則によれば、Map
を実装する任意の具象クラスもMap
です。つまり、キャストを行うことなく、任意のHashMap
型の変数をMap
型の変数に割り当てることができます。
この場合、キャストやデータの損失なしにmap1
をmap2
に割り当てることができます -
map2 = map1