Marcel B
Marcel B

Reputation: 3664

HashMap with StringKey problem?

I have a List of Foo Objects. If a name appears multiple times, I want to do something to the first Item with that name.

HashMap<String, Foo> map = new HashMap<String, Foo>();
for (Foo bar: this.FooList)
{
    if (!map.containsKey(bar.getName()))
    {
        map.put(bar.getName(), bar);
    }
    else
    {
        map.get(bar.getName()).doSomeThing();
    }
}

But this is not working, because every name (unique or not) gets thrown in that map. Does the HashMap check only for reference equality and not equality on key objects?

Upvotes: 1

Views: 7395

Answers (4)

Hank Gay
Hank Gay

Reputation: 71929

What exactly is the problem? The code adds the Foo if it hasn't been encountered before, and invokes an action on the Foo if it has been encountered before. What were you expecting it to do?

To answer your question at the end, HashMap relies on the hashCode and equals methods of the key objects.

UPDATE: Are you trying to invoke doSomeThing only on repeated Foos? If the name property of a Foo defines it's identity (and you properly implemented hashCode() and equals()) you should use R. Bemrose's solution. If name is not a unique property of Foos, the following code might help:

final Map<String, Foo> firstFooByName = new HashMap<String, Foo>();
final List<String> dupeNames = new ArrayList<String>();
for (final Foo foo: myFoos) {
    final String fooName = foo.getName();
    if (!firstFooByName.containsKey(fooName)) {
        firstFooByName.add(fooName)
    } else {
        dupeNames.add(fooName);
    }
}

for (final String fooName : dupeNames) {
    firstFooByName.get(fooName).doSomeThing();
}

Upvotes: 0

Powerlord
Powerlord

Reputation: 88786

Hmmm... if Foo implements equals() so that it only checks name, you could always do something like this:

Set<Foo> set = new HashSet<Foo>();
for (Foo bar: this.FooList)
{
    if (!set.add(bar)) {
       bar.doSomething();
    }
}

Which works because set.add(bar) will run bar.equals against every element already in the set, and return false if any of them are equal.

Edit: Since this is a HashSet, you should also implement hashCode(). Heck, you should always implement hashCode() if you're overriding equals anyway.

Upvotes: 1

Mark Byers
Mark Byers

Reputation: 837886

This is the code you need:

    HashMap<String, Foo> map = new HashMap<String, Foo>();
    for (Foo bar: this.FooList)
    {
        if (!map.containsKey(bar.getName()))
        {
            map.put(bar.getName(), bar);
        }
        else
        {
            Foo foo = map.get(bar.getName());
            if (foo != null)
                foo.doSomeThing();
            map.put(bar.getName(), null);
        }
    }

Here's a testbed for it:

import java.util.HashMap;
import java.util.ArrayList;

public class Example
{
    public static void main(String[] args)
    {
        new Example().run();
    }

    private ArrayList<Foo> FooList = new ArrayList<Foo>();

    public void run()
    {
        FooList.add(new Foo("abc", 1));
        FooList.add(new Foo("abc", 2));
        FooList.add(new Foo("def", 3));
        FooList.add(new Foo("abc", 4));
        FooList.add(new Foo("abc", 5));
        FooList.add(new Foo("ghi", 6));
        FooList.add(new Foo("def", 7));

        HashMap<String, Foo> map = new HashMap<String, Foo>();
        for (Foo bar: this.FooList)
        {
            if (!map.containsKey(bar.getName()))
            {
                map.put(bar.getName(), bar);
            }
            else
            {
                Foo foo = map.get(bar.getName());
                if (foo != null)
                    foo.doSomeThing();
                map.put(bar.getName(), null);
            }
        }
    }

    class Foo
    {
        public Foo(String name, int i)
        {
            this.name = name;
            this.i = i;
        }

        public String getName()
        {
            return name;
        }

        public void doSomeThing()
        {
            System.out.println(getName() + " " + i);
        }

        private String name;
        private int i;
    }
}

Output is:

abc 1
def 3

Upvotes: 3

Peter Recore
Peter Recore

Reputation: 14187

HashMap uses the .equals() method on the key object.

Upvotes: 0

Related Questions