Reputation: 381
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
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
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
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
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