agurylev
agurylev

Reputation: 381

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

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?
}

Upvotes: 26

Views: 1141

Answers (4)

baao
baao

Reputation: 73241

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.

Upvotes: 13

Eugene
Eugene

Reputation: 120858

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.

Upvotes: 0

Stephen C
Stephen C

Reputation: 718798

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.

Upvotes: 6

Boann
Boann

Reputation: 50041

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.)

Upvotes: 2

Related Questions