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

Почему Java ArrayList использует кастинг для каждого элемента вместо кастинга для каждого массива?

Что происходит внутри Java ArrayList<T> (и, вероятно, многих других классов), заключается в том, что существует внутренний Object[] array = new Object[n];, к которому записываются T Objects. Всякий раз, когда элемент считывается из него, выполняется листинг return (T) array[i];. Итак, бросок на каждом прочтении.

Интересно, почему это делается. Мне кажется, что они просто делают ненужные броски. Не было бы более логичным, а также немного быстрее просто создать T[] array = (T[]) new Object[n];, а затем просто return array[i]; без трансляции? Это всего лишь одно действие при создании массива, которое, как правило, намного меньше количества прочитанных.

Почему их метод предпочтительнее? Я не понимаю, почему моя идея не совсем лучше?

4b9b3361

Ответ 1

Это сложнее: generics стираются в байтовом коде, а стирание T[] - Object[]. Аналогично, возвращаемое значение get() становится Object. Чтобы сохранить целостность системы типов, проверенный листинг вставляется, когда класс фактически используется, то есть

Integer i = list.get(0);

будет стерто до

Integer i = (Integer) list.get(0);

Таким образом, проверка любого типа внутри ArrayList является избыточной. Но это действительно так, потому что как (T), так и (T[]) - неконтролируемые отбрасывания и не накладывают на них лишних затрат времени исполнения.

Можно написать проверенный ArrayList, который делает:

T[] array = Array.newInstance(tClass, n);

Это предотвратит загрязнение кучи, но по цене проверки избыточного типа (вы не можете подавить синтетический бросок в вызывающем коде). Это также потребовало бы, чтобы вызывающий пользователь предоставил ArrayList объекту класса типа элемента, который загромождает его api и затрудняет его использование в общем коде.

Изменить: почему запрещено создание общего массива?

Одна проблема заключается в том, что массивы проверены, а дженерики не отмечены. То есть:

Object[] array = new String[1];
array[0] = 1; // throws ArrayStoreException

ArrayList list = new ArrayList<String>();
list.add(1); // causes heap pollution

Следовательно, имеет значение тип компонента массива. Я предполагаю, что поэтому разработчики языка Java требуют от нас явного указания того, какой тип компонента использовать.

Ответ 2

Всякий раз, когда элемент считывается из него, выполняется листинг return (T) array[i];. Итак, бросок на каждом прочтении.

Общий - это проверка времени компиляции. Во время выполнения вместо этого используется тип T extends. В этом случае T неявно extends Object, так что у вас есть во время выполнения.

return (Object) array[i];

или

return array[i];

Не было бы более логичным, а также немного быстрее просто создать

T[] array = (T[]) new Object[n]

Не совсем. Снова во время выполнения это становится

Object[] array = (Object[]) new Object[n];

или

Object[] array = new Object[n];

Что вы на самом деле ловите рыбу?

T[] array = new T[n];

за исключением того, что это не скомпилируется, главным образом потому, что T не известно во время выполнения.

Что вы можете сделать, это

private final Class<T> tClass; // must be passed in the constructor

T[] array = (T[]) Array.newInstance(tClass, n);

только тогда массив действительно будет ожидаемым типом. Это может сделать чтение быстрее, но ценой записи. Основным преимуществом будет проверка с ошибкой, т.е. Вы остановите поврежденную коллекцию, а не подождите, пока не обнаружите, что она была повреждена, чтобы выбросить исключение.

Ответ 3

Я думаю, что это скорее вопрос стиля кода, а не производительности или безопасности типа (поскольку массив поддержки является закрытым)

java 5 ArrayList был реализован так, как вы предложили с массивом E[]. Если вы посмотрите на исходный код, вы увидите, что он содержит 7 (E []). Из java 6 ArrayList изменилось использование массива Object[], в результате чего было выполнено только 3 (E).

Ответ 4

Массив тоже объект. Здесь T[] array = (T[]) new Object[n] вы бросаете только (T []) тип объекта, а не элементы в массиве.