I have an object that stores some data in a list. The implementation could change later, and I don't want to expose the internal implementation to the end user. However, the user must have the ability to modify and access this collection of data. Currently I have something like this:
public List<SomeDataType> getData() {
return this.data;
}
public void setData(List<SomeDataType> data) {
this.data = data;
}
Does this mean that I have allowed the internal implementation details to leak out? Should I be doing this instead?
public Collection<SomeDataType> getData() {
return this.data;
}
public void setData(Collection<SomeDataType> data) {
this.data = new ArrayList<SomeDataType>(data);
}
It just depends, do you want your users to be able to index into the data? If yes, use List. Both are interfaces, so you're not leaking implementation details, really, you just need to decide the minimum functionality needed.
Returning a List is in line with programming to the Highest Suitable Interface.
Returning a Collection would cause ambiguity to the user, as a returned collection could be either: Set, List or Queue.
Independent of the ability to index into the list via List.get(int), do the users (or you) have an expectation that the elements of the collection are in a reliable and predictable order? Can the collection have multiples of the same item? Both of these are expectations of lists that are not common to more general collections. These are the tests I use when determining which abstraction to expose to the end user.
When returning an implementation of an interface or class that is in a tall hierarchy, the rule of thumb is that the declared return type should be the HIGHEST level that provides the minimum functionality that you are prepared to guarantee to the caller, and that the caller reasonably needs. For example, suppose what you really return is an ArrayList. ArrayList implements List and Collection (among other things). If you expect the caller to need to use the get(int x) function, then it won't work to return a Collection, you'll need to return a List or ArrayList. As long as you don't see any reason why you would ever change your implementation to use something other than a list -- say a Set -- then the right answer is to return a List. I'm not sure if there's any function in ArrayList that isn't in List, but if there is, the same reasoning would apply. On the other hand, once you do return a List instead of a Collection, you have now locked in your implementation to some extent. The less you put in your API, the less restriction you put on future improvements.
(In practice, I almost always return a List in such situations, and it has never burned me. But I probably really should return a Collection.)
Using the most general type, which is Collection, makes the most sense unless there is some explicit reason to use the more specific type - List. But whatever you do, if this is an API for public consumption be clear in the documentation what it does; if it returns a shallow copy of the collection say so.
Yes, your first alternative does leak implementation details if it's not part of your interface contract that the method will always return a List. Also, allowing user code to replace your collection instance is somewhat dangerous, because the implementation they pass in may not behave as you expect.
Of course, it's all a matter of how much you trust your users. If you take the Python philosophy that "we're all consenting adults here" then the first method is just fine. If you think that your library will be used by inexperienced developers and you need to do all you can to "babysit" them and make sure they don't do something wrong then it's preferable not to let them set the collection and not to even return the actual collection. Instead return a (shallow) copy of it.
It depends on what guarantees you want to provide the user. If the data is sequential such that the order of the elements matter and you are allowing duplicates, then use a list. If order of elements does not matter and duplicates may or may not be allowed, then use a collection. Since you are actually returning the underlying collection you should not have both a get and set function, only a get function, since the returned collection may be mutated. Also, providing a set function allows the type of collection to be changed by the user, whereas you probably want for the particular type to be controlled by you.
Were I concerned with obscuring internal representation of my data to an outside user, I would use either XML or JSON. Either way, they're fairly universal.
Related
If there is a reason to make a parameter not generic, is that a good approach?
Let's say I know in the method there takes place only access of members of list but not insertion should I force the developers to pass an ArrayList.
public void method(ArrayList<Integer> list)
{
// ......
}
as you can see developers have to pass a list of type ArrayList otherwise they get error.
should I force the developers to pass an ArrayList.
IMO, no. The developer might use the list somewhere else. Only he knows the actual usage of the list and it should be up to him to choose the best list implementation. If there is no actual reason to enforce a specific implementation, you should always use the List interface.
Generally speaking, there are two good (albeit possibly rare) reasons I can think of to do this.
First, if you want to use a method that's only present in a specific implementation. ArrayList doesn't seem to have too many useful methods that aren't already specified by the List or even Collection interfaces, but it's still a possibility.
Second, which is a slight variation on the previous reason, is if you want to convey some performance expectations of your method. For example, both the ArrayList and LinkedList classes have a get(int) method. ArrayList's implementation works in constant time (O(1)), while LinkedList's is linearly dependent on the size of the list (O(n)). If your method relies heavily on this method, you may not want to allow calling it with a LinkedList.
The main issue with this is that the caller probably hasn't typed their List as an ArrayList, so even if they do have an ArrayList, they would probably have to write an explicit cast at every call-site. That would make the caller's code messier.
Another issue is that ArrayList is not the only class which implements List and supports random access in O(1) time. For example, Arrays.asList returns a java.util.Arrays$ArrayList object which is not an instance of java.util.ArrayList. Alternatively, for example if I want to implement a sparse List using a hashtable, such that get takes O(1) time but it's not an ArrayList, then I wouldn't be able to supply my list to this method even though it meets the performance requirements.
My solution would be to check the argument at runtime:
if(list instanceof LinkedList) {
// ...
}
Then you can either log a warning to inform about the performance issue, or (if you must be prescriptive), throw an IllegalArgumentException to say the method should not be called with a LinkedList. This means the caller's mistake would be detected at runtime rather than compile-time, but it should be detected the very first time it's tested, so that is no great loss.
Another option, of course, is to take an array instead of a list as your parameter. That won't be suitable for all purposes, but it's e.g. how the standard library's binarySearch method ensures it takes a sequence which supports random access in O(1) time.
Suppose I have a private ArrayList or a LinkedList inside a class, that I will never assign new reference to it, or in other words this will never happen:
myLinkedList = anotherLinkedList;
So that I won't need to use setMyLinkedList(anotherLinkedList).
But! I need to add elements to it, or remove elements from it.
Should I write a new kind of setter to only, do the task of adding instead of setting, like myLinkedList.add(someElement)?
Or it is OK to do this by using getter, without disobeying Encapsulation principal?
getMyLinkedList().add(someElement)
( + Suppose I am going to lose my mark if I disobey encapsulation :-")
I don't think it a particularly great practice to do something like:
myObj.getMyList().add(x);
since you are exposing a private class variable in a non read only way, but that being said I do see it pretty frequently(I'm looking at you, auto generated classes). I would argue that instead of doing it that way, return an unmodifiable list and allow users of the class to add to the list via an explicit method:
public class MyClass{
private final List<String> myList = new ArrayList<String>();
public List<String> getList(){
return Collections.unmodifiableList(this.myList);
}
public void addToList(final String s){
this.myList.add(s);
}
}
EDIT After reviewing your comments, I wanted to add a bit about your setter idea:
I meant using that line of code inside a new kind of setter inside the class itself, like public void setter(someElement){this.myLinkedList.add(someElement);}
If I'm understanding you correctly, you are saying you want to expose a method that only adds to your list. Overall this is what I think you should be shooting for, and what many have outlined in the answers, however, labeling it as a setter is a bit misleading since you are not reassigning (setting) anything. That, and I strongly recommend returning a read only list from your getter method if possible.
I would suggest in this case it would be best to follow your Encapsulation principals and use a method for adding elements to the list. You have restricted access to your list by making it private so that other classes cannot directly access the datatype.
Let the class that stores your ArrayList have direct access to the list, but when other classes want to add to the list, use an add() method.
In general, you should not assume that the list being returned by the getter is the original one. It could be decorated or proxied for example.
If you want to prevent that a new list is set on the target object, you could define an add method on the target class instead.
As soon as you have a Collection of any kind, it is generally not a bad idea to add methods like add(), remove() to the interface of your class if it makes sense that clients can add or remove objects from your private list.
The reason why it is useful to have these extra methods implemented (it might seem like overkill, because after all those methods mostly just call the method on the Collection) is that you protect evil clients from doing things to your list you don't want them to do, because the interface of most Collections contain more than just the add() and remove() methods and mostly, you don't want clients to be messing around with things you can't control. Therefore the encapsulation principle is that important to your teacher.
Another plus: if at any time, you would decide that a certain condition must be met when an object is added to your list, this can easily be implemented in the method you already have. If you give a client access to the direct reference of your list, it is not easy at all to implement this kind of things (which are not rare).
Hope this helps
So you have a class containing a List field (it should be final, since you don't intend to assign to it), and you want to allow callers to add to the List, but not be able to replace it.
You could either provide a getter for the list:
public List<E> getMyList() {
return myList;
}
Or provide a method to add to that list:
public void addToMyList(E e) {
myList.add(e);
}
Both are valid design decisions, but which you use will depend on your use case. The first option gives callers direct access to the List, effectively making it public. This is useful when users will be modifying and working with the list repeatedly, but can be problematic as you can no longer trust the List is in any sort of reliable state (the caller could empty it, or reorder it, or even maliciously insert objects of a different type). So the first option should only be used when you intend to trust the caller.
The second option gives the caller less power, because they can only add one element at a time. If you want to provide additional features (insertion, add-all, etc.) you'll have to wrap each operation in turn. But it gives you more confidence, since you can be certain the List is only being modified in ways you approve of. This latter option also hides (encapsulates) the implementation detail that you're using a List at all, so if encapsulation is important for your use case, you want to go this way to avoid exposing your internal data structures, and only expose the behavior you want to grant to callers.
It depends on the application - both are acceptable. Take a good look at the class you're writing and decide if you want to allow users to directly access the contents of the list, or if you would prefer that they go through some intermediate process first.
For example, say you have a class ListEncrypter which holds your MyLinkedList list. The purpose of this class is to encrypt anything that is stored in MyLinkedList. In this case, you'd want to provide a custom add method in order to process the added item before placing it in the list, and if you want to access the element, you'd also process it:
public void add(Object element)
{
MyLinkedList.add(encrypt(element););
}
public Object get(int index)
{
return decrypt(MyLinkedList.get(index););
}
In this case, you clearly want to deny the user's access to the MyLinkedList variable, since the contents will be encrypted and they won't be able to do anything with it.
On the other hand, if you're not really doing any processing of the data (and you're sure you won't ever need to in the future), you can skip creating the specialized methods and just allow the user to directly access the list via the get method.
The question is framed for List but easily applies to others in the java collections framework.
For example, I would say it is certainly appropriate to make a new List sub-type to store something like a counter of additions since it is an integral part of the list's operation and doesn't alter that it "is a list". Something like this:
public class ChangeTrackingList<E> extends ArrayList<E> {
private int changeCount;
...
#Override public boolean add(E e) {
changeCount++;
return super.add(e);
}
// other methods likewise overridden as appropriate to track change counter
}
However, what about adding additional functionality out of the knowledge of a list ADT, such as storing arbitrary data associated with a list element? Assuming the associated data was properly managed when elements are added and removed, of course. Something like this:
public class PayloadList<E> extends ArrayList<E> {
private Object[] payload;
...
public void setData(int index, Object data) {
... // manage 'payload' array
payload[index] = data;
}
public Object getData(int index) {
... // manage 'payload' array, error handling, etc.
return payload[index];
}
}
In this case I have altered that it is "just a list" by adding not only additional functionality (behavior) but also additional state. Certainly part of the purpose of type specification and inheritance, but is there an implicit restriction (taboo, deprecation, poor practice, etc.) on Java collections types to treat them specially?
For example, when referencing this PayloadList as a java.util.List, one will mutate and iterate as normal and ignore the payload. This is problematic when it comes to something like persistence or copying which does not expect a List to carry additional data to be maintained. I've seen many places that accept an object, check to see that it "is a list" and then simply treat it as java.util.List. Should they instead allow arbitrary application contributions to manage specific concrete sub-types?
Since this implementation would constantly produce issues in instance slicing (ignoring sub-type fields) is it a bad idea to extend a collection in this way and always use composition when there is additional data to be managed? Or is it instead the job of persistence or copying to account for all concrete sub-types including additional fields?
This is purely a matter of opinion, but personally I would advise against extending classes like ArrayList in almost all circumstances, and favour composition instead.
Even your ChangeTrackingList is rather dodgy. What does
list.addAll(Arrays.asList("foo", "bar"));
do? Does it increment changeCount twice, or not at all? It depends on whether ArrayList.addAll() uses add(), which is an implementation detail you should not have to worry about. You would also have to keep your methods in sync with the ArrayList methods. For example, at present addAll(Collection<?> collection) is implemented on top of add(), but if they decided in a future release to check first if collection instanceof ArrayList, and if so use System.arraycopy to directly copy the data, you would have to change your addAll() method to only increment changeCount by collection.size() if the collection is an ArrayList (otherwise it gets done in add()).
Also if a method is ever added to List (this happened with forEach() and stream() for example) this would cause problems if you were using that method name to mean something else. This can happen when extending abstract classes too, but at least an abstract class has no state, so you are less likely to be able to cause too much damage by overriding methods.
I would still use the List interface, and ideally extend AbstractList. Something like this
public final class PayloadList<E> extends AbstractList<E> implements RandomAccess {
private final ArrayList<E> list;
private final Object[] payload;
// details missing
}
That way you have a class that implements List and makes use of ArrayList without you having to worry about implementation details.
(By the way, in my opinion, the class AbstractList is amazing. You only have to override get() and size() to have a functioning List implementation and methods like containsAll(), toString() and stream() all just work.)
One aspect you should consider is that all classes that inherit from AbstractList are value classes. That means that they have meaningful equals(Object) and hashCode() methods, therefore two lists are judged to be equal even if they are not the same instance of any class.
Furthermore, the equals() contract from AbstractList allows any list to be compared with the current list - not just a list with the same implementation.
Now, if you add a value item to a value class when you extend it, you need to include that value item in the equals() and hashCode() methods. Otherwise you will allow two PayloadList lists with different payloads to be considered "the same" when somebody uses them in a map, a set, or just a plain equals() comparison in any algorithm.
But in fact, it's impossible to extend a value class by adding a value item! You'll end up breaking the equals() contract, either by breaking symmetry (A plain ArrayList containing [1,2,3] will return true when compared with a PayloadList containing [1,2,3] with a payload of [a,b,c], but the reverse comparison won't return true). Or you'll break transitivity.
This means that basically, the only proper way to extend a value class is by adding non-value behavior (e.g. a list that logs every add() and remove()). The only way to avoid breaking the contract is to use composition. And it has to be composition that does not implement List at all (because again, other lists will accept anything that implements List and gives the same values when iterating it).
This answer is based on item 8 of Effective Java, 2nd Edition, by Joshua Bloch
If the class is not final, you can always extend it. Everything else is subjective and a matter of opinion.
My opinion is to favor composition over inheritance, since in the long run, inheritance produces low cohesion and high coupling, which is the opposite of a good OO design. But this is just my opinion.
The following is all just opinion, the question invites opinionated answers (I think its borderline to not being approiate for SO).
While your approach is workable in some situations, I'd argue its bad design and it is very brittle. Its also pretty complicated to cover all loopholes (maybe even impossible, for example when the list is sorted by means other than List.sort).
If you require extra data to be stored and managed it would be better integrated into the list items themselves, or the data could be associated using existing collection types like Map.
If you really need an association list type, consider making it not an instance of java.util.List, but a specialized type with specialized API. That way no accidents are possible by passing it where a plain list is expected.
I have a class with a private mutable list of data.
I need to expose list items given following conditions:
List should not be modifiable outside;
It should be clear for developers who use getter function that a list they get can not be modified.
Which getter function should be marked as recommended approach? Or can you offer a better solution?
class DataProcessor {
private final ArrayList<String> simpleData = new ArrayList<>();
private final CopyOnWriteArrayList<String> copyData = new CopyOnWriteArrayList<>();
public void modifyData() {
...
}
public Iterable<String> getUnmodifiableIterable() {
return Collections.unmodifiableCollection(simpleData);
}
public Iterator<String> getUnmodifiableIterator() {
return Collections.unmodifiableCollection(simpleData).iterator();
}
public Iterable<String> getCopyIterable() {
return copyData;
}
public Iterator<String> getCopyIterator() {
return copyData.iterator();
}
}
UPD: this question is from a real code-review discussion on the best practice for list getter implementation
The "best" solution actually depends on the intended application patterns (and not so much on "opinions", as suggested by a close-voter). Each possible solution has pros and cons that can be judged objectively (and have to be judged by the developer).
Edit: There already was a question "Should I return a Collection or a Stream?", with an elaborate answers by Brian Goetz. You should consult this answers as well before making any decision. My answer does not refer to streams, but only to different ways of exposing the data as a collection, pointing out the pros, cons and implications of the different approaches.
Returning an iterator
Returning only an Iterator is inconvenient, regardless of further details, e.g. whether it will allow modifications or not. An Iterator alone can not be used in the foreach loop. So clients would have to write
Iterator<String> it = data.getUnmodifiableIterator();
while (it.hasNext()) {
String s = it.next();
process(s);
}
whereas basically all other solutions would allow them to just write
for (String s : data.getUnmodifiableIterable()) {
process(s);
}
Exposing a Collections.unmodifiable... view on the internal data:
You could expose the internal data structure, wrapped into the corresponding Collections.unmodifiable... collection. Any attempt to modify the returned collection will cause an UnsupportedOperationException to be thrown, clearly stating that the client should not modify the data.
One degree of freedom in the design space here is whether or not you hide additional information: When you have a List, you could offer a method
private List<String> internalData;
List<String> getData() {
return Collections.unmodifiableList(internalData);
}
Alternatively, you could be less specific about the type of the internal data:
If the caller should not be able to do indexed access with the List#get(int index) method, then you could change the return type of this method to Collection<String>.
If the caller additionally should not be able to obtain the size of the returned sequence by calling Collection'size(), then you could return an Iterable<String>.
Also consider that, when exposing the less specific interfaces, you later have the choice to change the type of the internal data to be a Set<String>, for example. If you had guaranteed to return a List<String>, then changing this later may cause some headaches.
Exposing a copy of the internal data:
A very simple solution is to just return a copy of the list:
private List<String> internalData;
List<String> getData() {
return new ArrayList<String>(internalData);
}
This may have the drawback of (potentially large and frequent) memory copies, and thus should only be considered when the collection is "small".
Additionally, the caller will be able to modify the list, and he might expect the changes to be reflected in the internal state (which is not the case). This problem could be alleviated by additionally wrapping the new list into a Collections.unmodifiableList.
Exposing a CopyOnWriteArrayList
Exposing a CopyOnWriteArrayList via its Iterator or as an Iterable is probably not a good idea: The caller has the option to modify it via Iterator#remove calls, and you explicitly wanted to avoid this.
The solution of exposing a CopyOnWriteArrayList which is wrapped into a Collections.unmodifiableList may be an option. It may look like a superfluously thick firewall at the first glance, but it definitely could be justified - see the next paragraph.
General considerations
In any case, you should document the behavior religiously. Particularly, you should document that the caller is not supposed to change the returned data in any way (regardless of whether it is possible without causing an exception).
Beyond that, there is an uncomfortable trade-off: You can either be precise in the documentation, or avoid exposing implementation details in the documentation.
Consider the following case:
/**
* Returns the data. The returned list is unmodifiable.
*/
List<String> getData() {
return Collections.unmodifiableList(internalData);
}
The documentation here should in fact also state that...
/* ...
* The returned list is a VIEW on the internal data.
* Changes in the internal data will be visible in
* the returned list.
*/
This may be an important information, considering thread safety and the behavior during iteration. Consider a loop that iterates over the unmodifiable view on the internal data. And consider that in this loop, someone calls a function that causes a modification of the internal data:
for (String s : data.getData()) {
...
data.changeInternalData();
}
This loop will break with a ConcurrentModificationException, because the internal data is modified while it is being iterated over.
The trade-off regarding the documentation here refers to the fact that, once a certain behavior is specified, clients will rely on this behavior. Imagine the client does this:
List<String> list = data.getList();
int oldSize = list.size();
data.insertElementToInternalData();
// Here, the client relies on the fact that he received
// a VIEW on the internal data:
int newSize = list.size();
assertTrue(newSize == oldSize+1);
Things like the ConcurrentModificationException could have been avoided if a true copy of the internal data had been returned, or by using a CopyOnWriteArrayList (each wrapped into a Collections.unmodifiableList). This would be the "safest" solution, in this regard:
The caller can not modify the returned list
The caller can not modify the internal state directly
If the caller modifies the internal state indirectly, then the iteration still works
But one has to think about whether so much "safety" is really required for the respective application case, and how this can be documented in a way that still allows changes to the internal implementation details.
Typically, Iterator is used only with Iterable, for the purpose of for-each loop. It'll be pretty odd to see a non-Iterable type contains a method returning Iterator, and it maybe upsetting to the user that it cannot be used in for-each loop.
So I suggest Iterable in this case. You could even have your class implements Iterable if that makes sense.
If you want to jump on the Java 8 wagon, returning a Stream probably is a more "modern" approach.
By encapsulation rules you had to always return an unmodifiable list, in your case is a design rule, so return the Collections.unmodifiableCollection, and you don't need to name the method as getUnmodifiable, use getter naming convenction and use Javadoc to tell other developer what kind of list you return and why...careless users will be alerted with an exception!!
In the JDK, there's Collection.emtpyList() and Collection.emptySet(). Both in their own right. But sometimes all that is needed is an empty, immutable instance of Collection. To me, there's no reason to chose one over the other as both implement all operations of Collection in an efficient way and with the same results. Yet each time I need such an empty collection I ponder which one to use for a second of two.
I do not expect to gain a deeper understanding of the collections framework from an answer to this question but maybe there's a subtle reason I could use to justify choosing one over the other without thinking about it ever again.
An answer should state at least one reason preferring one of Collection.emtpyList() and Collection.emptySet() over the other in a context where they're functionally equivalent. An answer is better if the stated reason is near the top of this list:
There's a case where the type system is happier with one over the other (e.g. type inference allows shorter code with one than the other).
There is a performance difference, maybe in some special case (e.g. if the empty collection is passed as an argument to some of the collection framework's static or instance methods like Collections.sort() or Collection.removeAll()).
Choosing one over the other "makes more sense" in the general case, if you think about it.
Examples where this question arises
To give some context, here are two examples where I am in need of an empty, unmodifiable collection.
This is an example of an API that allows creating some object by optionally specifying a collection of objects that are used in the creation. The second method just calls the first one with an empty collection:
static void createObjectWithTheseThings(Collection<Thing> things) {
...
}
static void createObjectWithoutAnyThings() {
createObjectWithTheseThings(Collections.emptyXXX());
}
This is an example of an Entity with state represented by an immutable collection stored in a non-final field. On initialization the field should be set to an empty collection:
class Example {
// Initialized to an empty collection.
private Collection<T> containedThings = Collections.emptyXXX();
...
}
Unfortunately I don't have an answer that will make the top of your priority list but if I were you I'd settle on
Collections.emptySet
Type inference was your first priority but I don't know if the choice can/should influence that given you were looking for an emptyCollection()
On the second priority, think about any api that takes in a collection which performs differently (accidentally/intentionally) based on the sub-interfaces of the concrete object passed in. Aren't they more likely to offer varied performance based on the concrete implementations (as with an ArrayList or LinkedList) instead? The empty set/list are not modeled on any empty data structures anyway; they are dummy implementations - hence no real difference
Based on java's modelling of these interfaces (which admittedly is not ideal), a Collection is very similar to a Set. In fact I think the methods are almost exactly the same. Logically too it looks OK with List being the specific-sub type that adds additional ordering concerns.
Now Collection and Set looking very similar(java-wise) brings up a question. If you are using a Collection type, it is clear it is not a list you want. Now the question is are you sure you don't mean a Set. If you don't, then are you using something like a Bag (surely there must be concrete instances which are not empty in the overall logic). So if you are concerned with say a Bag, then shouldn't it be up to the Bag api to provide an emptyBag() method? Just wondering. btw, I'd stick with emptySet() in the meantime :)
For the emptyXXX(), it really doesn't matter at all - since they are both empty (and they are unmodifieable, so they always stay empty) it doesn't matter at all. They will be equally suited to all operations Collection offers.
Take a look at what Collections really gives you there: Special implementations (the instances are shared across calls!). All relevant operations are dummy implementations that either return a constant result or immediately throw. Even iterator() is just a dummy with no state.
It wont make any notable difference at all.
Edit: You could say for the special case of emptyList/Set, they are semantically and complexity-wise the same at the Collecton interface level. All operations available on Collection are implemented by emptySet/List as O(1) operations. And since they're following both the contract defined by Collection, they are semantically identical too.
The only situation I can imagine this making a difference is if the code that will use your Collection does something like this:
Collection<T> collection = ...
List<T> asAList;
if (collection instanceof List) {
asAList = (List<T>) collection;
} else {
asAList = new ArrayList<T>(collection);
}
Obviously in a case like this you would want to use emptyList(), while if the secret target type was a Set, you'd want emptySet().
Otherwise, in terms of what "makes more sense", I agree with #ac3's logic that a generic Collection is like a Bag, and an empty immutable Set and empty immutable Bag are pretty much the same thing. However, a person very used to using immutable lists might find those easier to think of.