2

So , I want to make a map of Lists of Strings to Strings, but I cannot get it to work properly:

this is all the code I have done, and until i can find out why, I cannot progress:

Map<List<String>, String> test = new HashMap<List<String>, String>();
test.put( new ArrayList<String>(), "s1");
test.put( new ArrayList<String>(), "s2");
test.put( new ArrayList<String>(), "s3");

System.out.println(test.size());

i get 1, it should be 3!! Why is only one object getting added when I made 3 calls, for 3 separate objects? I know the danger of accidentally adding in the same object to a collection, but I specifically created a new ArrayList for each put, thus creating a total brand new object.

So why is there only one object in the Map then? Thanks!

dreamcrash
  • 47,137
  • 25
  • 94
  • 117

5 Answers5

3

The ArrayList#hashCode() is returning the same for all of them. If you look at the sourcing for it: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/AbstractList.java#AbstractList.hashCode%28%29

You can see that should there be no elements, or all the same elements, that the hashCode would be the same.

Fix that up and it should work fine. Use either a Map or change the hashCode in one way or another.

  • thanks!how do i change the hashCode? would a TreeMap fix the problem? – Alexx Shadenk Feb 12 '13 at 05:27
  • That would work great. You could override ArrayList and just change the hashCode method. For example, make it have a unique ID such as for example: time of creation, a randomly generated number, etc. –  Feb 12 '13 at 06:35
3

Try this:

Map<String,List<String>> test = new HashMap<String,List<String>>();
test.put("s1", new ArrayList<String>());
test.put("s2", new ArrayList<String>());
test.put("s3", new ArrayList<String>());

System.out.println(test.size());

Note that a map is a key-value relation. For this reason, you may want too use the String as a key and the ArrayList as the values, instead of the other way around. This way, if you add 3 different Strings, each one will have a different hash value (hashcode). Thus, you will have 3 different keys in your Map.

Note also that:

put

public Object put(Object key, Object value) Associates the specified value with the specified key in this map. If the map previously contained a mapping for this key, the old value is replaced.

This is why you are getting 1, instead of 3, because you were adding the same object new ArrayList<String>().

Take look in more detail into the Class HashM specifications.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117
1

You use ArrayList as the key, try

    System.out.println(new ArrayList<String>().equals(new ArrayList<String>()));

it prints

true
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
  • 1
    The general contract for hashCode is: If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result. – Evgeniy Dorofeev Feb 12 '13 at 06:38
0

The best approach would be to use the String object as key and the List as value.

Have a look at what java docs say here.

And if at all you want to add the ArrayList as key then overriding equals method should suffice.

The post here gives good insights. I have put some which I liked from that post.

Overriding equals for two ArrayLists:

 public boolean equals(List<String> one, List<String> two){
    if (one == null && two == null){
        return true;
    }

    if((one == null && two != null) 
      || one != null && two == null
      || one.size() != two.size()){
        return false;
    }

    //to avoid messing the order of the lists we will use a copy
    //as noted in comments by A. R. S.
    one = new ArrayList<String>(one); 
    two = new ArrayList<String>(two);   

    Collections.sort(one);
    Collections.sort(two);      
    return one.equals(two);
}
Community
  • 1
  • 1
Sam
  • 2,352
  • 4
  • 32
  • 45
0

I'm not sure why you need the list as the key and the string as the value, but you are basically using the same 'key' for each put operation since the hashcode is the same for any empty list. Like the other answers mention, it's probably best to switch the list as the value and the string as the key. If the list changes, any future attempts at .get will return null

   public static void main(String...args) {
       Map<List<String>, String> test = new HashMap<List<String>, String>();
       List<String> bob = new ArrayList<>();
       bob.add("asdf");
       test.put( new ArrayList<String>(), "s1");
       test.put( bob, "s2");

       System.out.println(test.size());
       System.out.println(test.get(bob));
   }

outputs

2
s2

When a new item gets added like

   public static void main(String...args) {
       Map<List<String>, String> test = new HashMap<List<String>, String>();
       List<String> bob = new ArrayList<>();
       bob.add("asdf");
       test.put( new ArrayList<String>(), "s1");
       test.put( bob, "s2");      
       bob.add("aabbcc");

       System.out.println(test.size());
       System.out.println(test.get(bob));
   }

The get will not work, since the hash of the list has changed. The output in this case would be:

2
null
scott
  • 974
  • 7
  • 10