help with serializing a HashMap

Discussion in 'Java' started by Aryeh.Friedman@gmail.com, Nov 17, 2005.

  1. Guest

    I have the following class that attempts to save a
    HashMap<T1,ArrayList<T2>>

    public class Roles
    {
    public Roles()
    {
    contributers=new
    HashMap<Contributer,ArrayList<Role>>();
    }
    public void addRole(Contributer contributer,Role role)
    {
    ArrayList<Role> r=contributers.get(contributer);
    if(r==null) {
    r=new ArrayList<Role>();
    } else r=contributers.get(contributer);
    r.add(role);
    contributers.put(contributer,r);
    }
    public ArrayList<Role> getRoles(Contributer contributer)
    {
    return contributers.get(contributer);
    }
    public void save()
    {
    FileUtils.serialize("contributers",contributers);
    }
    public void load()
    {

    contributers=FileUtils.deserialize("contributers",contributers.getClass());
    }
    public void reset()
    {
    contributers=new
    HashMap<Contributer,ArrayList<Role>>();
    }
    public String toString()
    {
    String s=new String();
    s+=contributers;
    return s;
    }
    private HashMap<Contributer,ArrayList<Role>> contributers;
    }

    When I run it against the following test code I get the output below:
    public class RolesTest extends TestCase
    {
    public void setUp()
    {
    mc=new MockContributer();
    mr=new MockRole();
    mrl=new ArrayList<MockRole>();
    mcl=new ArrayList<MockContributer>();
    mrl.add(mr);
    mcl.add(mc);
    }
    public void testAdd() {
    Roles roles=new Roles();
    roles.addRole(mc,mr);
    System.out.println(roles.getRoles(mc));
    roles.save();
    roles.reset();
    roles.load();
    System.out.println(roles.getRoles(mc));
    }
    private MockContributer mc;
    private MockRole mr;
    private ArrayList<MockContributer> mcl;
    private ArrayList<MockRole> mrl;
    }

    Output:

    [Name: mock role]
    null

    For reference here are the MockContributer, MockRole, Contributer,
    Role, FileUtils and IOUtils (which FIleUtils.[de]serialize calls on):

    public class MockContributer extends Contributer
    {
    public MockContributer()
    {
    super("aryeh");
    }
    private static final long serialVersionUID=0;
    }

    public class MockRole extends Role
    {
    public MockRole()
    {
    super("mock role");
    }
    private static final long serialVersionUID=0;
    }

    public abstract class Contributer implements Serializable
    {
    public Contributer(String id)
    {
    this.id=id;
    }
    public String getId()
    {
    return id;
    }
    public String toString()
    {
    String s=new String();

    s+="authorId: "+id;

    return s;
    }
    private String id;
    }

    public abstract class Role implements Serializable
    {
    public Role(String name)
    {
    this.name=name;
    }
    public String getName()
    {
    return name;
    }
    public String toString()
    {
    String s=new String();
    s+="Name: "+name;
    return s;
    }

    private static final long serialVersionUID=0;
    private String name;
    }

    public class FileUtils
    {
    public static void serialize(String fname,Serializable obj)
    {
    FileOutputStream out=null;
    try {
    out=new FileOutputStream(fname);
    } catch(FileNotFoundException e) {
    throw new FileError(e);
    }
    IOUtils.serialize(out,obj);
    try {
    out.close();
    } catch(IOException e) {
    throw new FileError(e);
    }
    }
    public static <T extends Serializable> T deserialize(String
    fname,Class<? extends T> c)
    {
    FileInputStream in=null;
    T obj=null;
    try {
    in=new FileInputStream(fname);
    } catch(FileNotFoundException e) {
    throw new FileError(e);
    }
    obj=IOUtils.deserialize(in,c);
    try {
    in.close();
    } catch(IOException e) {
    throw new FileError(e);
    }
    return obj;
    }
    }

    public class IOUtils
    {
    public static void serialize(OutputStream out,Serializable obj)
    {
    try {
    ObjectOutputStream stream=new
    ObjectOutputStream(out);
    stream.writeObject(obj);
    } catch(IOException e) {
    throw new IOError(e);
    }
    }
    public static <T> T deserialize(InputStream in,Class<? extends
    T> c)
    {
    try {
    ObjectInputStream stream=new
    ObjectInputStream(in);
    T obj=c.cast(stream.readObject());
    return obj;
    } catch(IOException e) {
    throw new IOError(e);
    } catch(ClassNotFoundException e) {
    throw new IOError(e);
    }
    }
    }
    , Nov 17, 2005
    #1
    1. Advertising

  2. zero Guest

    "" <> wrote in
    news::

    > I have the following class that attempts to save a
    > HashMap<T1,ArrayList<T2>>
    >


    <code snipped>

    The first step when debugging should always be to exactly pinpoint the
    problem. Print out the HashMap before you serialize it, after you
    serialize it, before you deserialize, and after you deserialize. Before
    you deserialize should be empty, all the rest should be the same. This
    will help you see where the problem occurs: when adding items to the Map,
    when serializing, or when deserializing. Run tests that show the data in
    every stage of the program, that way you can be sure when it goes wrong.

    I suspect the problem is when adding items. You'll probably need to
    implement boolean equals(Object o) and int hashCode() in the Contributer
    class.
    zero, Nov 17, 2005
    #2
    1. Advertising

  3. Guest

    Actually no here is the test I was using (removed the two middle prints
    to make the output/bug a little clearer):

    public void testAdd() {
    Roles roles=new Roles();
    roles.addRole(mc,mr);
    System.out.println(roles.getRoles(mc));
    System.out.println(roles);
    roles.save();
    System.out.println(roles);
    roles.reset();
    System.out.println(roles);
    roles.load();
    System.out.println(roles);
    System.out.println(roles.getRoles(mc));
    }


    Output:

    [Name: mock role]
    {authorId: aryeh=[Name: mock role]}
    {authorId: aryeh=[Name: mock role]}
    {}
    {authorId: aryeh=[Name: mock role]}
    null
    , Nov 17, 2005
    #3
  4. zero Guest

    "" <> wrote in
    news::

    > Actually no here is the test I was using (removed the two middle
    > prints to make the output/bug a little clearer):
    >
    > public void testAdd() {
    > Roles roles=new Roles();
    > roles.addRole(mc,mr);
    > System.out.println(roles.getRoles(mc));
    > System.out.println(roles);
    > roles.save();
    > System.out.println(roles);
    > roles.reset();
    > System.out.println(roles);
    > roles.load();
    > System.out.println(roles);
    > System.out.println(roles.getRoles(mc));
    > }
    >
    >
    > Output:
    >
    > [Name: mock role]
    > {authorId: aryeh=[Name: mock role]}
    > {authorId: aryeh=[Name: mock role]}
    > {}
    > {authorId: aryeh=[Name: mock role]}
    > null
    >


    Ok, your problem is not with the serialization. As you can see on the
    second to last line in your output, the HashMap gets read fine. Your
    problem is that the rules.getRoles(mc) returns null.

    Why?

    To figure that out you first have to consider how objects are saved in a
    HashMap. The hashMap uses the key's hashCode method to determine where
    to put the object in its internal table. In your case, that's
    MockContributer's hashCode function. However, you didn't implement
    hashCode, so it's using the function inherited from Object. Object's
    JavaDoc says "... typically implemented by converting the internal
    address of the object into an integer"

    So, what happens is that the object gets saved in a location determined
    by its address. You then save the Map to disk, and delete the reference
    to it. When you reload the Map, it - and all of its elements - have a
    different address, and thus a different hashCode. You then try to get
    back an object from the Map, but you're using an old reference - and an
    old hashCode. The table entry in the Map corresponding to this old
    hashCode is now empty, so you're getting a null object.

    And the solution? If you don't actually need to use old references to
    get items from the deserialized Map, you could ignore this problem, and
    instead for example use an Iterator to get all the elements in the Map.
    However, this may lead to problems later on, and is probably not
    optimally performant.

    The other, and better, solution would be to override the hashCode()
    function of Contributer. This could be as simple as having an int
    argument in Contributer. Other people here recently gave me some great
    hints on how to write a good hashCode method. Have a look at:

    http://groups.google.be/group/comp.lang.java.programmer/browse_thread/th
    read/32c74ea4ecc8e31f/6d7e56f773d37aa6
    zero, Nov 17, 2005
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Michael
    Replies:
    1
    Views:
    5,326
    Michael Berg
    Feb 22, 2004
  2. Vince Darley
    Replies:
    4
    Views:
    4,418
    emilchacko
    Mar 2, 2010
  3. Tricia Williams

    Serializing a HashMap

    Tricia Williams, Sep 20, 2005, in forum: Java
    Replies:
    5
    Views:
    8,818
    Roedy Green
    Sep 20, 2005
  4. Rakesh
    Replies:
    10
    Views:
    12,171
    Mike Schilling
    Apr 8, 2008
  5. Durango

    Need help serializing data using C

    Durango, Apr 8, 2012, in forum: C Programming
    Replies:
    10
    Views:
    764
    Sunus Lee
    Apr 18, 2012
Loading...

Share This Page