LoginSignup
3
4

More than 5 years have passed since last update.

HashMapのKEY値を変更したら、getメソッドで何を取得するか?

Last updated at Posted at 2016-01-05

JavaのHashMapのKeyの値を変更したら、getメソッドで何を取得できるのか、議論しましたが、意見がまとまらないので、コードでやってみます

結果:Keyの値を変更したら、NULLが取得しました

まずテスト用のの車クラスを作りました。
色とモデルだけです。equelsとhashcodeをoverrideをしました。

public static class Car {

    private String color;

    private String model;


    /**
     * colorを取得します。
     *
     * @return color color
     */
    public final String getColor() {

        return color;
    }


    /**
     * colorを設定します。
     *
     * @param color
     *            color
     */
    public final void setColor( String color ) {

        this.color = color;
    }


    /**
     * modelを取得します。
     *
     * @return model model
     */
    public final String getModel() {

        return model;
    }


    /**
     * modelを設定します。
     *
     * @param model
     *            model
     */
    public final void setModel( String model ) {

        this.model = model;
    }


    /*
     * (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {

        final int prime = 31;
        int result = 1;
        result = prime * result + ( ( color == null ) ? 0 : color.hashCode() );
        result = prime * result + ( ( model == null ) ? 0 : model.hashCode() );
        return result;
    }


    /*
     * (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals( Object obj ) {

        if ( this == obj ) {
            return true;
        }
        if ( obj == null ) {
            return false;
        }
        if ( !( obj instanceof Car ) ) {
            return false;
        }
        Car other = (Car) obj;
        if ( color == null ) {
            if ( other.color != null ) {
                return false;
            }
        } else if ( !color.equals( other.color ) ) {
            return false;
        }
        if ( model == null ) {
            if ( other.model != null ) {
                return false;
            }
        } else if ( !model.equals( other.model ) ) {
            return false;
        }
        return true;
    }


    /*
     * (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {

        return "Car [color="
                        + color
                        + ", model="
                        + model
                        + ", getColor()="
                        + getColor()
                        + ", getModel()="
                        + getModel()
                        + ", hashCode()="
                        + hashCode()
                        + ", getClass()="
                        + getClass()
                        + ", toString()="
                        + super.toString()
                        + "]";
    }

}

テストコード:

public static void main( String[] args ) {
HashMap hm = new HashMap<>();
Car bmw = new Car();
bmw.setColor( "red" );
bmw.setModel( "x3" );
hm.put( bmw, "bmw" );
bmw.setColor( "blue" );
System.out.println( hm.get( bmw ) );
bmw.setColor( "red" );
System.out.println( hm.get( bmw ) );
}

出力結果:
    null
    bmw

blueに変更したら、取れなくなるが、redに戻したら、bmwが返ってきました。

なぜこうなっているか、HashMapのgetメソッドを見てみます。

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(**hash(key)**, key)) == null ? null : e.value;
}

/**
* Implements Map.get and related methods
*
* @param hash hash for key
* @param key the key
* @return the node, or null if none
/
final Node getNode(int hash, Object key) {
Node[] tab; Node first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
*
(first = tab[(n - 1) & hash]) != null) {** //indexを取ってます
if (first.hash == hash && // always check first node //hashcodで一致判断
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}

hash()メソッドで、Keyのhashcodeに対して、もう一回Hash計算しています。
この計算後のHashcodeで、node配列のindexを取っています。
Keyの値を変更したので、HashCodeも変わるので、indexが取れなくなるので、Nullを返しています。
たとえ、計算されたindexにNodeが ちょうどあるとしても、Nodeに保持されるhashもkeyのhashcodeでなく、Hash()メッソドで計算された値なので、NULLを返すはずです
Putのソースも見てみます。

/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with key, or
* null if there was no mapping for key.
* (A null return can also indicate that the map
* previously associated null with key.)
/
public V put(K key, V value) {
return putVal(
hash*(key), key, value, false, true); //putするときも、hash()を使用しています。
}

/**
 * Implements Map.put and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = **newNode(hash, key, value, null);**//新しいnodeを追加するとき、hashを使用しています。
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

hash()も見てみます

/**
* Computes key.hashCode() and spreads (XORs) higher bits of hash
* to lower. Because the table uses power-of-two masking, sets of
* hashes that vary only in bits above the current mask will
* always collide. (Among known examples are sets of Float keys
* holding consecutive whole numbers in small tables.) So we
* apply a transform that spreads the impact of higher bits
* downward. There is a tradeoff between speed, utility, and
* quality of bit-spreading. Because many common sets of hashes
* are already reasonably distributed (so don't benefit from
* spreading), and because we use trees to handle large sets of
* collisions in bins, we just XOR some shifted bits in the
* cheapest possible way to reduce systematic lossage, as well as
* to incorporate impact of the highest bits that would otherwise
* never be used in index calculations because of table bounds.
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

結論:HashMapのKeyとして使用されるオブジェクトの値は変更しないことです
   でも、これはあくまで個人的な意見、間違ったところがありましたら、ご了承ください。

   

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4