Подтвердить что ты не робот

Можно ли узнать, является ли какой-либо список фиксированным размером или нет?

Можно ли узнать, является ли какой-либо список фиксированным размером или нет? Я имею в виду, например, этот код:

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

возвращает фиксированный размер List, поддерживаемый массивом. Но возможно ли понять программно, если List является фиксированным или нет, не пытаясь добавить/удалить элементы и перехватить исключение? Например:

try {
    list.add("c");
}
catch(UnsupportedOperationException e) {
    // Fixed-size?
}
4b9b3361

Ответ 1

Список, созданный с помощью String[] на

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

будет иметь Arrays как охватывающий класс, тогда как один, созданный, например, new ArrayList(), не будет иметь класс включения. Поэтому необходимо проверить, был ли List создан в результате вызова Arrays.toList():

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

Помните, что этот метод основан на недокументированном поведении. Он сломается, если они добавят еще один вложенный список подкласса в класс Arrays.

Ответ 2

Можно ли узнать, фиксирован ли какой-либо список или нет?

В теории - Нет. Фиксированная размерность - это появившееся свойство реализации класса списка. Вы можете определить, имеет ли этот список это свойство, пытаясь добавить элемент.

И обратите внимание, что простой поведенческий тест не будет надежно различать список фиксированного размера и ограниченный список или список, который был постоянно или временно доступен только для чтения.


На практике список фиксированного размера обычно будет иметь обычный класс. Вы можете проверить класс объекта, чтобы убедиться, что он или не является определенным классом. Поэтому, если вы поймете, какие классы будут использоваться для реализации списков фиксированного размера в вашей базе кода, вы можете проверить, имеет ли конкретный список фиксированный размер.

Например, метод Arrays.asList(...) возвращает объект List, фактический класс которого java.util.Arrays.ArrayList. Это частный вложенный класс, но вы можете использовать отражение, найти его, а затем использовать Object.getClass().equals(...) для проверки.

Однако этот подход является хрупким. Ваш код может сломаться, если была изменена реализация Arrays, или если вы начали использовать другие формы фиксированного размера.

Ответ 3

Нет.

API-интерфейс списка идентичен независимо от того, является ли список расширяемым или нет, то, что было преднамеренным.

В API списка также нет ничего, что позволяет запросить его для определения этой функции.

Вы не можете полностью надежно определить эту информацию путем отражения, потому что вы будете зависеть от внутренних деталей реализации и потому, что существует неограниченное количество классов, которые потенциально фиксированы. Например, помимо Arrays.asList существует также Arrays.asList().subList, который, как оказалось, возвращает другой класс. Также могут быть обертки вокруг базового списка, например Collections.checkedList, Collections.synchronizedList и Collections.unmodifiableList. Существуют также другие списки фиксированного размера: Collections.emptyList, Collections.singletonList и Collections.nCopies. Вне стандартной библиотеки есть такие вещи, как Guava ImmutableList. Это также довольно тривиально, чтобы вручную раскрыть список для чего-то, расширив AbstractList (для списка фиксированного размера вам нужно реализовать только size() и get(int)).

Даже если вы обнаружите, что ваш список не имеет фиксированного размера, спецификация List.add позволяет ему отказаться от элементов по другим причинам, Например, обертки Collections.checkedList выбрасывают ClassCastException для элементов нежелательного типа.

И даже если вы знаете, что ваш список расширяем и позволяет произвольные элементы, это не значит, что вы хотите его использовать. Возможно, он синхронизирован или не синхронизирован или не является сериализуемым, или это медленный список ссылок, или имеет другое качество, которое вам не нужно.

Если вы хотите контролировать тип, изменчивость, сериализуемость или безопасность потоков в списке, или вы хотите быть уверены, что ни один другой код не сохранил ссылку на него, практика заключается в том, что вы сами создаете новую, Это не дорого, когда это необходимо (memcopies пылают быстро), и это позволяет вам более точно рассуждать о том, что ваш код действительно будет выполнять во время выполнения. Если вы действительно хотите избежать создания ненужных копий, попробуйте использовать белый список вместо классов списка черных списков. Например:

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

(Примечание. Это использует getClass вместо instanceof, потому что instanceof также будет истинным для любых странных подклассов ArrayList.)

Ответ 4

В java-9 есть неизменные коллекции, но до сих пор нет общей аннотации @Immutable или общего интерфейса маркера, который мы могли бы запросить для получения этой информации.

Самый простой способ, который я могу представить, - просто получить имя класса такого экземпляра:

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

но это все еще зависит от внутренних деталей, поскольку оно запрашивает имя общего класса ImmutableCollections, которое не является общедоступным и, очевидно, может меняться без предупреждения.