Java Design – Designing an Iterable but Immutable Collection

data structuresdesignjava

My program models a sport tournament which has one or many events or categories. The class Event has members like players or courts as a List, as well as some dictionaries using the class Map.

So far accessing publicly those members can be done through their respective getters, but I realized this is a bad practice because it allows modifications through the collection object retrieved with the getter. And I wouldn't want this.

I would like to limit the set of operations allowed on these members and avoid the situation described above.

One might think just declaring these members as final would do, but it doesn't. These members can still be modified, just instead of freely doing that directly with the native List or Map methods, it's done via my classes' methods.

So let's say I clamp the collections set of operations allowed on these collections and I make wrapping classes for my lists and maps, so instead of having this:

private List<Player> players;
private List<Court> courts;

private Map<Player, List<Timeslot>> playersUnavailable;

I could have something like:

private CustomList<Player> players;
private CustomList<Court> courts;

private CustomMap<Player, List<Timeslot>> playersUnavailable;

The name of the classes would probably need to be different, I just can't think of something better at the moment.

Say we just want reader methods for these classes. So we could have a method to get the size and another to get a particular element of the internal collection:

public class CustomList<T> {
    private final List<T> list;
    public CustomList<T>(List<T> l) { list = l; }
    public int size() { return list.size(); }
    public int get(int i) { return list.get(i); }
}

However, now I can't iterate through items of a collection like I normally would:

for (Player player : players)
    System.out.println(player);

Although I could use get() and size() but it feels a bit rudimentary.

What could I do to make these collections iterable but preserving the immutability rules my design has? (As I said it's not strictly immutability, since you can do it using the pertinent methods)

Overall, should I rethink my design differently? Does what I'm trying to do make sense? Could I achieve it differently, in a better way?

I guess I could say what I am looking for are read-only collections, am I?

Best Answer

It appears you have two requirements here:

  1. Event exposes collections, but callers cannot modify those collections.

  2. Event can modify its collections, but only through the Event class.

It sounds like you need to use immutable collections, for example, java.util.Collections.unmodifiableList(List list). However, you do not want to store an immutable collection as the state in Event. It could work like this:

public class Event {

  private List<Player> players;

  public List<Player> getPlayers() {
    return Collections.unmodifiableList(players);
  }

  public void addPlayer(Player p) {
    players.add(p);
  }
}

This allows modifying the collections through Event, while still allowing callers to get an immutable reference suitable for iteration. The call to Collections.unmodifiableList(players) wraps the list players and does not copy it.

If this needs to be thread-safe then there are additional concerns to address here, but that was not a requirement. It sounds more like your Event class needs to handle invariants and data integrity between multiple collections that model the same objects. That concern should easily be addressable using this technique.