26

Is it possible to find out if some a list is fixed size or not? I mean, for example this code:

String[] arr = {"a", "b"};
List<String> list = Arrays.asList(array);

returns fixed size List backed by an array. But is it possible to understand programmatically if List is fixed-size or not without trying to add/remove elements and catching the exception? For example:

try {
    list.add("c");
}
catch(UnsupportedOperationException e) {
    // Fixed-size?
}
Michael
  • 41,989
  • 11
  • 82
  • 128
agurylev
  • 381
  • 2
  • 10
  • Why can't you use the exception? –  Jul 07 '17 at 16:19
  • 6
    @RC. He didn't say he couldn't but it's not ideal if there's another way to do it. Exceptions are slow and for exceptional conditions. – Michael Jul 07 '17 at 16:20
  • 7
    Close voter, please explain how this is unclear. I understand perfectly well what he's asking. – Michael Jul 07 '17 at 16:22
  • nice first question! But please show us what you've tried so far(Don't worry I'm not the close voter, I'm an up-voter) –  Jul 07 '17 at 16:24
  • can you just use `if(list.size() > 0) {`? –  Jul 07 '17 at 16:25
  • @Michael I didn't say he did, I was just wondering –  Jul 07 '17 at 16:26
  • 5
    @Tommy He doesn't want to know if his list is empty, but if adding/removing to the list is allowed. – OH GOD SPIDERS Jul 07 '17 at 16:27
  • https://stackoverflow.com/questions/5207162/fixed-size-list-in-java Check this. –  Jul 07 '17 at 16:29
  • Your question makes no sense. It is not valuable to know if you can add or delete elements from a list. Either assume that you can and accept an exception or assume that you cant. – DwB Jul 07 '17 at 16:31
  • 2
    I actually don't think there is a 100% sure way to check if the List is of fixed size. But just to throw the idea out there: You could use reflection to check if the list implementation overrides the add/remove methods or just uses the ones from the AbstractList. `boolean addNotSupported = (java.util.AbstractList.class==list.getClass().getMethod("add", Object.class).getDeclaringClass());` – OH GOD SPIDERS Jul 07 '17 at 16:33
  • 7
    It is kind of odd to have a code path that has special treatment for a read-only list if the code intends to insert. Perhaps the OP can explain what *he's really trying to do* and why he needs to support read-only lists simultaneously with lists that can be updated. – selbie Jul 07 '17 at 16:35
  • 2
    This is a significant code smell. Better to just e.g. _always_ copy to an `ArrayList` or the like. – Louis Wasserman Jul 07 '17 at 17:13
  • I can think of a scenario where this would be useful: when you want to have a precondition check at the start of a method. That would not be a code smell. – Raedwald Jul 12 '17 at 08:33
  • Unfortunately, no. Good API design remains a foreign concept to the Java world. – raianmr Jan 14 '23 at 06:35

4 Answers4

14

A list created from a String[] by

List<String> list = Arrays.asList(array);

will have Arrays as enclosing class, while one created by for example new ArrayList() won't have the enclosing class. So the following should work to check if the List was produced as a result of calling Arrays.toList():

static <T> boolean wasListProducedAsAResultOfCallingTheFunctionArrays_asList(List<T> l) {
    return Arrays.class.equals(l.getClass().getEnclosingClass());
}

Beware that this method relies on undocumented behavior. It will break if they added another nested List subclass to the Arrays class.

Mark Rotteveel
  • 100,966
  • 191
  • 140
  • 197
baao
  • 71,625
  • 17
  • 143
  • 203
  • 2
    Good one. `isFixedSize` is a bit of misnomer for that function, though. – Michael Jul 07 '17 at 16:44
  • @Michael thanks. Do you have a better name in mind? Feel free to edit :-) – baao Jul 07 '17 at 16:45
  • 11
    [maybe `wasListProducedAsAResultOfCallingTheFunctionArrays_asList`](https://martinfowler.com/bliki/TwoHardThings.html)?? – Michael Jul 07 '17 at 16:50
  • 2
    @Michael Took it :D – baao Jul 07 '17 at 16:52
  • 3
    Just a code style suggestion... Remove the or and just use `Arrays.class.equals(l.getClass().getEnclosingClass())`. Or just `Arrays.class == l.getClass().getEnclosingClass()` – fps Jul 07 '17 at 17:32
  • 2
    Good suggestion @FedericoPeraltaSchaffner, didn't think about that when I wrote it earlier. – baao Jul 07 '17 at 17:34
  • This fails to return true for wrappers around the base list, such as `Collections.checkedList`, `Collections.synchronizedList` or `Collections.unmodifiableList`. It also fails to return true for `Arrays.asList().subList` or other types of fixed-size list like `Collections.emptyList` or `Collections.singletonList`, or other immutable lists from other utility libraries. – Boann Jul 08 '17 at 00:06
  • @Boann Or for short: It does *only* what the (updated) method name suggests - and not what the question originally was about... – Marco13 Jul 08 '17 at 00:47
  • this test would fail for java-9 immutable collections – Eugene Jul 09 '17 at 10:23
6

Is it possible to find out if some list is fixed size or not?

In theory - No. Fixed sizedness is an emergent property of the implementation of a list class. You can only determine if a list has that property by trying to add an element.

And note that a simple behavioral test would not reliably distinguish between a fixed sized list and a bounded list or a list that was permanently or temporarily read-only.


In practice, a fixed sized list will typically have a different class to an ordinary one. You can test the class of an object to see if it or isn't a specific class. So if you understand what classes would be used to implement fixed sized lists in your code-base, then you can test if a specific list is fixed sized.

For example the Arrays.asList(...) method returns a List object whose actual class is java.util.Arrays.ArrayList. That is a private nested class, but you could use reflection find it, and then use Object.getClass().equals(...) to test for it.

However, this approach is fragile. Your code could break if the implementation of Arrays was modified, or if you started using other forms of fixed sized list as well.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
2

No.

The List API is identical regardless of whether a List is expandable or not, something that was deliberate.

There is also nothing in the List API that allows you to query it to determine this feature.

You can't completely reliably determine this information by reflection, because you will be depending on internal details of the implementation, and because there is an unbounded number of classes that are potentially fixed-size. For example, in addition to Arrays.asList, there is also Arrays.asList().subList, which happens to return a different class. There can also be wrappers around the base list like Collections.checkedList, Collections.synchronizedList and Collections.unmodifiableList. There are also other fixed-size lists: Collections.emptyList, Collections.singletonList, and Collections.nCopies. Outside the standard library, there are things like Guava's ImmutableList. It's also pretty trivial to hand-roll a list for something by extending AbstractList (for a fixed-size list you need only implement the size() and get(int) methods).

Even if you detect that your list is not fixed-size, the specification of List.add allows it to refuse elements for other reasons. For example, Collections.checkedList wrappers throw a ClassCastException for elements of unwanted type.

And even if you know your list is expandable, and allows arbitrary elements, that doesn't mean you want to use it. Perhaps it's synchronized, or not synchronized, or isn't serializable, or it's a slow linked list, or has some other quality that you don't want.

If you want control over the type, mutability, serializability, or thread-safety of the list, or you want to be sure that no other code has kept a reference to it, the practice is that you create a new one yourself. It's not expensive to do so when unnecessary (memcopies are blazing fast), and it lets you reason more definitely about your code will actually do at runtime. If you'd really like to avoid creating unnecessary copies, try whitelisting instead of blacklisting list classes. For example:

if (list.getClass() != ArrayList.class) {
    list = new ArrayList<>(list);
}

(Note: That uses getClass instead of instanceof, because instanceof would also be true for any weird subclasses of ArrayList.)

Boann
  • 48,794
  • 16
  • 117
  • 146
0

There are immutable collections in java-9, but there is still no common @Immutable annotation for example or a common marker interface that we could query to get this information.

The simplest way I can think of would be simply to get the name of the class of such an instance:

String nameList = List.of(1, 2, 3).getClass().getName();
System.out.println(nameList.contains("Immutable"));

but that still relies on internal details, since it queries the name of the common class ImmutableCollections, that is not public and obviously can change without notice.

Eugene
  • 117,005
  • 15
  • 201
  • 306