38

次のような定数のマップがあります。

private static Map<String, Character> _typesMap =
        new HashMap<String, Character>() {
        {
            put ("string", 'S');
            put ("normalizedString", 'N');
            put ("token", 'T');
            // (...)
        }

Collections.unmodifiableMap()このマップを作成するために本当に を使用する必要がありますか? それを使用する利点は何ですか?それらが実際には一定になっていないという明らかな事実以外に、それを使用しないことの欠点はありますか?

4

3 に答える 3

72

Collections.unmodifiableMap は、マップが変更されないことを保証します。メソッド呼び出しから内部マップの読み取り専用ビューを返したい場合に最も役立ちます。例:

class A {
    private Map importantData;

    public Map getImportantData() {
        return Collections.unmodifiableMap(importantData);
    }
}

これにより、クライアントがデータを変更する危険を冒さない高速な方法が得られます。マップのコピーを返すよりもはるかに高速で、メモリ効率が高くなります。クライアントが本当に戻り値を変更したい場合は、それを自分でコピーできますが、コピーへの変更は A のデータには反映されません。

マップ参照を他の誰にも返さない場合は、不変にすることに偏執的でない限り、わざわざ変更不可にしないでください。おそらく、変更しないことを信頼できます。

于 2010-10-22T16:56:11.747 に答える
33

「Collections.unmodifiableMap はマップが変更されないことを保証する」という上記の Cameron Skinner の声明は、実際には一般的に部分的にしか当てはまりませんが、問題の特定の例ではたまたま正確です (Character オブジェクトが不変であるためのみ)。例を挙げて説明します。

Collections.unmodifiableMap は実際には、マップに保持されているオブジェクトへの参照を変更できないという保護のみを提供します。これは、返されるマップへの「プット」を制限することによって行われます。ただし、 Collections.unmodifiableMap はマップのコンテンツのコピーを作成しないため、元のカプセル化されたマップはクラスの外部から変更できます。

Paulo が投稿した質問では、幸いなことに、マップに保持されている Character オブジェクトは変更できません。ただし、一般的にこれは当てはまらない可能性があり、 Collections.unmodifiableMap によってアドバタイズされる変更不可能性が唯一の安全策であってはなりません。たとえば、以下の例を参照してください。

import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SeeminglyUnmodifiable {
   private Map<String, Point> startingLocations = new HashMap<>(3);

   public SeeminglyUnmodifiable(){
      startingLocations.put("LeftRook", new Point(1, 1));
      startingLocations.put("LeftKnight", new Point(1, 2));
      startingLocations.put("LeftCamel", new Point(1, 3));
      //..more locations..
   }

   public Map<String, Point> getStartingLocations(){
      return Collections.unmodifiableMap(startingLocations);
   }

   public static void main(String [] args){
     SeeminglyUnmodifiable  pieceLocations = new SeeminglyUnmodifiable();
     Map<String, Point> locations = pieceLocations.getStartingLocations();

     Point camelLoc = locations.get("LeftCamel");
     System.out.println("The LeftCamel's start is at [ " + camelLoc.getX() +  ", " + camelLoc.getY() + " ]");

     //Try 1. update elicits Exception
     try{
        locations.put("LeftCamel", new Point(0,0));  
     } catch (java.lang.UnsupportedOperationException e){
        System.out.println("Try 1 - Could not update the map!");
     }

     //Try 2. Now let's try changing the contents of the object from the unmodifiable map!
     camelLoc.setLocation(0,0);

     //Now see whether we were able to update the actual map
     Point newCamelLoc = pieceLocations.getStartingLocations().get("LeftCamel");
     System.out.println("Try 2 - Map updated! The LeftCamel's start is now at [ " + newCamelLoc.getX() +  ", " + newCamelLoc.getY() + " ]");       }
}

この例を実行すると、次のように表示されます。

The LeftCamel's start is at [ 1.0, 3.0 ]
Try 1 - Could not update the map!
Try 2 - Map updated! The LeftCamel's start is now at [ 0.0, 0.0 ]

startingLocations マップはカプセル化されており、getStartingLocations メソッドで Collections.unmodifiableMap を利用することによってのみ返されます。ただし、上記のコードの「Try 2」に示されているように、任意のオブジェクトにアクセスしてから変更すると、このスキームは覆されます。マップに保持されているオブジェクト自体が不変である場合、Collections.unmodifiableMap にのみ依存して真に変更不可能なマップを提供できると言えば十分です。そうでない場合は、マップ内のオブジェクトをコピーするか、可能であればオブジェクトのモディファイア メソッドへのアクセスを制限する必要があります。

于 2014-03-03T20:12:18.380 に答える
-2

Map をラップするのは、呼び出し元がコレクションを変更しないようにするためです。これはテストでは便利ですが、実際にはこの種のバグが見つかるはずです。本番環境ではあまり役に立たないかもしれません。簡単な回避策は、独自のラッパーを使用することです。

public static <K,V> Map<K,V> unmodifiableMap(Map<K,V> map) {
   assert (map = Collections.unmodifiableMap(map)) != null;
   return map;
}

これは、アサーションがオンになっている場合にのみマップをラップします。

于 2010-10-22T17:29:40.400 に答える