0

In doSomething method I've created a new Map of User objects. Than I've added a new book in the list of books and set the new name for the user. When I call this method the original Map of users users is changed. Why?

Book.java

public class Book {

    private String name;

    public Book() {
    }
    //getters and setters
}

User.java

public class User {

    private String firstName;
    private String lastName;
    private List<Book> listOfBooks;

    public User() {
    }

    public User(String firstName, String lastName, List<Book> listOfBooks) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.listOfBooks = listOfBooks;
    }
    //getters and setters
}

ListOfBook.java

public class ListOfBook {

private final List<Book> books;

public ListOfBook() {
    books = new ArrayList<>();
    books.add(new Book("The Lord of the Rings"));
    books.add(new Book("The Little Prince"));
}

public List<Book> getBooks() {
    return books;
    }
}

ListOfUser.java

public class ListOfUsers {

    private static ListOfUsers instance;
    private final Map<String, User> users;

    ListOfBook books = new ListOfBook();

    private ListOfUsers() {
        users = new LinkedHashMap<>();
        users.put("1", new User("X", "Y", books.getBooks()));
        users.put("2", new User("Z", "N", books.getBooks()));
    }

    public static ListOfUsers getInstance() {
        if (instance == null) {
            instance = new ListOfUsers();
        }
        return instance;
    }

    public Map<String, User> getUsers() {
        return users;
    }
}

Controller.java

public class Controller {

    private final Map<String, User> users;

    public Controller() {
        users = ListOfUsers.getInstance().getUsers();
    }

    public void doSomething() {
        Map<String, User> newUsers = new LinkedHashMap<>();
        newUsers.putAll(users);
        printUsers(users);
        User user = newUsers.get("1");
        List<Book> listOfBooks = user.getListOfBooks();
        listOfBooks.add(new Book("The Da Vinci Code"));
        user.setFirstName("N.N.");
    }

    public static void printUsers(Map<String, User> users) {
        for (Map.Entry<String, User> user : users.entrySet()) {
            System.out.print(user.getValue().getFirstName() + " ");
        }
        System.out.println();
    }
}

main.java

public class Example {

    public static void main(String[] args) {
        Controller controller = new Controller();
        controller.doSomething();
        controller.doSomething();
        ListOfUsers listOfUsers = ListOfUsers.getInstance();
        Map<String, User> users = listOfUsers.getUsers();
        List<Book> listOfBooks = users.get("1").getListOfBooks();
        for (int i = 0; i < listOfBooks.size(); i++) {
            System.out.println(listOfBooks.get(i).getName());
        }
        Controller.printUsers(users);
    }
}
myanmar
  • 81
  • 1
  • 2
  • 11
  • Read the doc for Map; does it say anywhere that a Map is not mutable?; And anyway, what do you mean by mutable? That you can add/remove keys or change the value associated with a key? Then yes. Or are you talking about a _reference_ to a Map? If the latter, no amount of immutability will help you anyway. – fge Mar 19 '15 at 19:56
  • It's a little unclear what you're asking here, what is it you expect to happen that is different from what you're seeing? – Adam Parkin Mar 19 '15 at 19:57
  • My mistake. I am talking about the reference. It seems that I have two reference at the same object. Any change in one Map causes the change in another, right? But I won't this effect. How to get rid of this in my code? – myanmar Mar 19 '15 at 19:59
  • you have to make a deep copy of your map i.e. the list of books in your user map has to been copied as well (like you do with putAll) – wgitscht Mar 19 '15 at 20:01
  • just a sidenote, i would recommend to synchronize `public static ListOfUsers getInstance()` if you plan to use this e.g. in an app server. – wgitscht Mar 19 '15 at 20:02
  • Thanks for your sidenote. I already knew that. It will be a desktop app. So I need a deep copy of everything. For the list I can write: **List books = new ArrayList<>(original)** But what to do with the User object: **User user = newUsers.get("1")** when I set the new name it change the User object in my original users map? – myanmar Mar 19 '15 at 20:11

0 Answers0