I override the equals method in the Friends class, yet I do not see the value "Spring 2002". Rather I see the value "null" instead. Did I miss something in my code?

package com.n2n.blotter.core.junit;
import java.util.*;

public class Birthdays {
    public static void main(String[] args){
    	Map<Friends, String> hm = new HashMap<Friends, String>();
    	hm.put(new Friends("Charis"), "Summer 2009");
    	hm.put(new Friends("Draumer"),"Spring 2002");
    	System.out.println(hm.get(new Friends("Draumer")));
    }
}

class Friends{
	String name;
	Friends(String n){
		this.name = n;
	}
	public boolean equals(Object obj){
		Friends f = (Friends)obj;
		if(this.name==f.name)
			return true;
		return false;
	}
}

Edited 5 Years Ago by solomon_13000: n/a

"System.out.println(hm.get(new Friends("Draumer")));"

You haven't overridden the HashCode method, which is what the HashMap uses to hash the objects. Since you haven't overridden it, it's using the default which I think is calculated from the object's address in memory.

If you want this object's identity to be based on name, you could use name.hashCode for your hashCode. That ought to get you the effect you're looking for, if I'm reading your example right.

By the way, "==" is not the way to compare Strings. Can you guess what the correct method is? :)

Actually, James, it turns out the hashCode method is key here. I wasn't sure if it would matter, so I checked. (Three cheers for ignorance!)
I assume the point of the exercise was to force the HashMap to return the "wrong" object (an object that had not in fact been placed there) as a demonstration of one form of equality.
Following code does in fact return "FOO", as desired. Comment out the hashcode, and it returns null.

import java.util.HashMap;

public class testHashMap
{
   public static void main (String[] args)
   {
      HashMap <Bar, String> foo = new HashMap<Bar, String>();
      foo.put(new Bar("foo"), "FOO");

      System.out.println(foo.get(new Bar("foo")));

   }
}

class Bar
{
   String name;

   public Bar(String s)
   {
      this.name=s;
   }

   public String name()
   {
      return name;
   }

   public boolean equals (Object o)
   {
      
      return false;
      return this.name().equals(((Bar)o).name());
   }

   public int hashCode()
   {
      return name.hashCode();
   }
}

Second try.
Yes Jon is right, the HashMap get method searches for the key by comparing hashcodes and then double-checks by testing for == or equals() in case of hash collisions. The new Friends that you create in the call to get() isn't the same Object as any of the keys, so the default hash method returns a different hash, and no match is found. To make this work the way you want you need to ensure that two Friends created with the same name also have the same hashCodes - which, as Jon says, you can do by overriding hashCode() to return the hashcode of the name String.
You also need an equals that works. Yours happens to work in this particular case by pure luck. You test for == (ie exactly the same Object) but the compiler has been kind and optimised lines 8 and 9 to create only one instance of the String "Draumer" and use it for both lines. In general two Strings that happen to conatin the sequence of characters won't test equal with ==. The String class contains a method that tests for equals like you need (hint).

Some confusing parallel posting here Jon! I posted 1/2 hour a go a post that was rubbish. I realised that a bit later and withdrew it, then while I was checking the source code for HashMap and writing a more correct post, you posted in response to my original.
Anyway, yes, you are completely right.

I suspect that the reason the == test works as often as it does with Strings is due to "interning" of the String. The jvm creates only one instance of a given String on the heap, and points all references to that string of characters at that one instance. Speculating wildly, I would guess this is why, or one reason why Strings have to be immutable. Imagine if you had three references to "apple" and changing one of them to "Microsoft" changed the others as well! Instead, what happens is that a new String is created, and one of the references which formerly pointed to "apple" now points to the new String, so we're fine.

The implication of this is that two Strings which both contain the same characters are in fact likely to point to the same place in memory, and therefore to return a true value in response to an == comparison.

I suspect that the reason the == test works as often as it does with Strings is due to "interning" of the String. The jvm creates only one instance of a given String on the heap, and points all references to that string of characters at that one instance. Speculating wildly, I would guess this is why, or one reason why Strings have to be immutable. Imagine if you had three references to "apple" and changing one of them to "Microsoft" changed the others as well! Instead, what happens is that a new String is created, and one of the references which formerly pointed to "apple" now points to the new String, so we're fine.

The implication of this is that two Strings which both contain the same characters are in fact likely to point to the same place in memory, and therefore to return a true value in response to an == comparison.

Yes, that's right for String literals. The problem hits you when you read a String for (eg) a file or a socket. OP's equals will work as right up to the point where he saves his Map of Friends on a file and loads it back in.

This article has been dead for over six months. Start a new discussion instead.