1. ホーム
  2. java

[解決済み] JavaにおけるHashMapオブジェクトとMapオブジェクトの違いは何ですか?

2022-03-23 01:53:22

質問

私が作成する以下のマップの違いは何ですか(別の質問で、人々が一見同じように使っているように答えていたので、違うのか/どう違うのか気になります)。

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 というのも、クラスを書くときにこの構造を使うのが適切だと思うからです。

その後、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 ではなく HashMapFoo . 更新する Foo を変更します。 HashMap から TreeMap . 今すぐ SpecialFoo はもうコンパイルできないのです。 Foo を提供すると言っていた。 HashMap を提供していましたが、現在は TreeMaps の代わりに そこで、私たちは SpecialFoo このようなことは、コードベース全体に波及する可能性があります。

よっぽどの理由がない限り、私の実装では HashMap (と宣言しておけばよかったのです。 getThingsgetMoreThings を返すだけなので Map<String, Object> それ以上具体的に説明することはありません。実際、他のことをする正当な理由がない限りは Foo を宣言する必要がありそうです。 thingsmoreThings として 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 のコントラクトは、彼女のコードに影響を与えなかったでしょう。

それができない場合もあるし、具体的に説明しなければならない場合もある。しかし、そうしなければならない理由がない限り、最も特殊性の低いインターフェイスを選ぶようにしましょう。