Java LinkedHashMap: what's the difference in these two?

EDIT: The entire code and database creation script can be found from http://gitorious.org/scheator . The database script is in Schema/.

I have the following Java code:

A LinkedHashMap defined in an abstract class as

LinkedHashMap<Object, Data> list;

A descendant class that initializes this list like this:

list = new LinkedHashMap<Integer, Data>();

I add items like this:

    String id = rs.getString(FIELDS[0]);
    String name = rs.getString(FIELDS[1]);
    Data team = new Data(Integer.parseInt(id.trim()), name);
    list.put(id, team);

Now when I do this:

    System.err.println("delete() count: " + list.size());

    System.err.println("delete() key value " + key);
    Data obj;
    obj = (Data)list.remove(key);
    deletedList.put(key, obj);
    System.err.println("delete() count: " + list.size());

Nothing is removed from the list, i.e. the first and last prints print the same size(). The key is also correct (I have verified there is an item by that id).

However, and this is my question, if I add the values like this:

    Integer id = rs.getInt(FIELDS[0]);
    String name = rs.getString(FIELDS[1]);
    Data team = new Data(id, name);
    list.put(id, team);

The code works! Shouldn't parseInt() produce a similar key to getInt()? Why does the second version work but the first doesn't? I spent a good hour debugging this until I found the reason and I still can't figure out the reason.

Answers


Yanamon is right. It's pretty clear when you look at the diff:

             while (rs.next()) {
-                String id = rs.getString(FIELDS[0]);
+                Integer id = rs.getInt(FIELDS[0]);
                 String name = rs.getString(FIELDS[1]);
-                Data team = new Data(Integer.parseInt(id.trim()), name);
+                Data team = new Data(id, name);
                 list.put(id, team);

Note that in the original version, an int (auto-boxed to Integer) is being passed into the Data constructor. But id, which is being putted, is still a String.


First example:

String id = rs.getString(FIELDS[0]);

Second example:

Integer id = rs.getInt(FIELDS[0]);

I can't say for sure since I can't see the rest of the code, but if the key variable is an Integer in this call:

obj = (Data)list.remove(key);

then the remove will only work if the object was put into the map using an Integer and that is why it is only working when the id is integer when you call the put method. The String "123" does not equal the integer 123.

Also I am assuming that you just missed a line in your first example but there was no call to list.put(id, team) but that could also be the source of your problems


There should be no difference, but there are a number of things that are not clear from your example:

  • deletedList does not refer to the list object
  • the records in your database that are being used are the same in both cases (perhaps in the first a different int is being used that is already in the Map)

Autoboxing may also be complicating the issue. Replace Integer id in the second sample with int id to pass the same arguments to your Data constructor.

Maybe you could post up the complete code such that we can reproduce the scenario accurately?


Update

You are using String values as keys in your original code. You then have an Object key in your remove(key) method, so I expect you are passing an Integer to the method at this point. A String will not match an Integer as a key, which explains why your remove was not working.

If you use generics to specify your HashMap (LinkedHashMap<Integer, Team> instead of <Object, Team>) this kind of error can't happen - the compiler will say something like

The method put(Integer, Object) in the type HashMap<Integer,Object> is not applicable for the arguments (String, String)


My guess is that int the second case you cast it explicitly into an Integer

Integer id = rs.getInt(FIELDS[0]);

while on the first case it remains an int

Integer.parseInt(id.trim())

from the javadoc of parseInt

static int  parseInt(String s) 
Parses the string argument as a signed decimal integer.

If I were you I would inspect the contents of the LinkedHashMap using a debugger, before and after your put and before and after your remove. Step into the remove() method (the source code is part of the JDK) and see what it is doing. Odds are your code is not adding or removing the object correctly. It's hard to see here because the code sample is incomplete.

As for rs.getInt() and Integer.parseInt(), the first is database-vendor specific (I assume rs is a ResultSet), and thus they may not have the same behaviour. However, once the Integer key is created (you can verify this with your debugger) it should be equivalent for HashMap or LinkedHashMap purposes. But your code sample complicates things further; you are using rs.getString() and then Integer.parseInt(). While I would be surprised if this happened, it's possible that the database driver is formatting the id column into a string that confuses parseInt(). To me it's far more readable to just do rs.getInt().


Need Your Help

Getting Notifications of iOS notifications center

ios iphone ios7

Can I get the Notifications of the iOS notifications center? I have a RF that needs to read all the notifications of the iPhone.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.