0

So... all is in code:

// get vector...
SignVector v = ...;

//print to console: [1058, 5, 820 in flat]
System.out.println(v);

//size: 1
System.out.println("size: " + signs.size());

//check all elements...
for (Entry<SignVector, FakeSign> entry : signs.entrySet())
{
    // get key
    SignVector key = entry.getKey();

    //print to console: [1058, 5, 820 in flat] (YaY! it's that key! like v)
    System.out.println(key);
    if (key.equals(v))
    {
        // print: "YaY: " 
        System.out.println("YaY: [1058, 5, 820 in flat]"+key);
    }
}
//So second test... just get it from map: null
System.out.println(signs.get(v));

Why that return null?
In JavaDocs is written that: map.get using key.equals(k) so why my code return good object, but map.get return null?

Map: private final Map<SignVector, FakeSign> signs = new HashMap<>()



Equals method form SignVector for @home user

@Override
public boolean equals(Object obj)
{
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    SignVector other = (SignVector) obj;
    // w can't be null so I skip that
    System.out.print(w.getName() + ", " + other.w.getName() + ", " + (w.getName().equals(other.w.getName()))); // this same
    if (!w.getName().equals(other.w.getName()))
        return false;
    if (x != other.x)
        return false;
    if (y != other.y)
        return false;
    if (z != other.z)
        return false;
    return true;
}

But this method works good, always return that I want, x,y,z is int, and w is custom object.

user2250333
  • 133
  • 3
  • 17
  • 1
    The fact that the key is equal does not mean it has a value. The value associated with the key may be null in the HashMap<,>. Show us the code where you insert the values. – ThaMe90 May 01 '14 at 14:10
  • Which map are you using? Is it HashMap? If so how is hashcode for `SignVector` calculated? Can hash change depending on values stored in vector? Did you modified vector used as key? – Pshemo May 01 '14 at 14:11
  • Value isn't null, I don't override hashcode, and `SignVector` is immutable (all fields are final) – user2250333 May 01 '14 at 14:13
  • 2
    @user2250333: Please show `SignVector.equals()` method... – home May 01 '14 at 14:13
  • @home I added... but this method work perfect for me, always return that I want. – user2250333 May 01 '14 at 14:18
  • Probably also need to see the `.equals()` method for whatever class `w` is as well. – JonK May 01 '14 at 14:18
  • 3
    "*I don't override hashcode*" [if you are overriding `equals` you should also override `hashCode` method](http://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java), especially if you are going to use it as key in HashMap. [General rule is that if `a.equals(b)` then `a.hashCode()==b.hashCode()`](http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java). – Pshemo May 01 '14 at 14:24
  • @JonK I don't have access to this, so I changed this method to other... (result this same) – user2250333 May 01 '14 at 14:24
  • @Pshemo After 2 years of learn Java... that is first time when I need use hashcode, always works perfectly without it. But anyway... that works, thanks! (YaY, copy-paste) But that still weird for me... why that return other hashcode for this same object. – user2250333 May 01 '14 at 14:36
  • 1
    @user2250333 It returns a different hashcode because the default implementation is based on the memory adress of the object. Since you created 2 brand new object they don't point in the same place in the memory and thus have two different hashcode values. – Alexis C. May 01 '14 at 14:37
  • 1
    @user2250333 It seems that your `hashCode` has been inherited from `Object` which means its value may not be identical for two different objects even if they are `equal` (since hashCode is native method its implementation may be different for different OS but people tend to believe that result of native hashcode method is based on location in memory where instance has been created) so two objects (even if they are `equal`) by default have two different hashcodes which prevents HashMap from matching them as same key. – Pshemo May 01 '14 at 14:41

2 Answers2

4

The javadoc is a bit misleading, but it's relying on the fact that if you implement equals, you should also implement hashcode to be consistent. As the doc states:

Many methods in Collections Framework interfaces are defined in terms of the equals method. For example, the specification for the containsKey(Object key) method says: "returns true if and only if this map contains a mapping for a key k such that (key==null ? k==null : key.equals(k))."

This specification should not be construed to imply that invoking Map.containsKey with a non-null argument key will cause key.equals(k) to be invoked for any key k.

Implementations are free to implement optimizations whereby the equals invocation is avoided, for example, by first comparing the hash codes of the two keys. (The Object.hashCode() specification guarantees that two objects with unequal hash codes cannot be equal.)

More generally, implementations of the various Collections Framework interfaces are free to take advantage of the specified behavior of underlying Object methods wherever the implementor deems it appropriate.

Let's take a look a the underlying implementation of get for an HashMap.

314  public V get(Object key) {
315      if (key == null)
316          return getForNullKey();
317      int hash = hash(key.hashCode());
318      for (Entry<K,V> e = table[indexFor(hash, table.length)];
319           e != null;
320           e = e.next) {
321          Object k;
322          if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
323              return e.value;
324      }
325      return null;
326  }

You see that is uses the hashcode of the object to find the possible entries in the table and THEN it uses equals to determine which value it has to return. Since the entry is probably null, the for loop is skipped and get returns null.

Override hashCode in your SignVector class to be consistent with equals and everything should work fine.

Community
  • 1
  • 1
Alexis C.
  • 91,686
  • 21
  • 171
  • 177
  • Hyym... After 2 years of learn Java... that is first time when I need use hashcode, always works perfectly without it. But anyway... that works, thanks! – user2250333 May 01 '14 at 14:34
  • 2
    @user2250333 If you use data structures that relies on hashcode to store the objects, you should be consistent with your class. As a rule of thumb, if you override equals, override hashcode (and vice-versae). – Alexis C. May 01 '14 at 14:35
  • But that still weird for me... why that return other hashcode for this same object. (I FakeSign have this same SignVector, I adding it to map like that: `map.put(fakeSign.getSignVector(), fakeSign)` so that should be this same object? – user2250333 May 01 '14 at 14:37
  • 1
    @user2250333 The problem is that you probably created a brand new `SignVector` object and trying to get the value mapped with in your map. If you used `map.put(fakeSign.getSignVector(), fakeSign); System.out.println(map.get(fakeSign.getSignVector());` it should print the fakeSign object mapped associated with (assuming `getSignVector` doesn't return a new `SignVector` each time you call it). – Alexis C. May 01 '14 at 14:48
1

From the javadocs:

If this map permits null values, then a return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.

Unless you share with us how you built the map, we can't help you if this is the case. The code you shared should otherwise be working just fine.

http://docs.oracle.com/javase/7/docs/api/java/util/Map.html#get%28java.lang.Object%29

jgitter
  • 3,396
  • 1
  • 19
  • 26