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

Не понимаю исходный код массива Array.copyOf

Мне трудно понять исходный код Arrays.copyOf.

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}
  • Что это за проверка строки?

    (Object)newType == (Object)Object[].class
    
  • В чем разница между (T[]) new Object[newLength] и (T[]) Array.newInstance(newType.getComponentType(), newLength). почему Array.newInstance недостаточно хорош для обоих случаев?

  • Эта следующая строка компилируется, но сбой во время выполнения (как и ожидалось). Когда следует использовать этот метод?

    Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 
    
4b9b3361

Ответ 1

Что это за проверка строки? (Object)newType == (Object)Object[].class

Он проверяет простое равенство (вероятно, с целью микро-оптимизации, но об этом позже).

Необычное литье необходимо, потому что Class<Object[]> (тип Object[].class) и Class<? extends T[]> являются несравнимыми типами. В принципе, для сравнения равенства с == для компиляции одна из сторон должна быть подтипом или супертипом другого.

т.е. мы не можем сделать:

// doesn't compile
// this expression can never evaluate to true
(new Integer(0) == new Float(0f))

Правила для типичных типов немного сложнее, и есть несколько случаев, когда сравнение не компилируется, но оно все равно может быть оценено как true.

Причина Class<Object[]> не является супертипом Class<? extends T[]>, несмотря на то, что Object[] является супертипом всех типов массивов объектов, заключается в том, что Java-дженерики являются инвариантными без наличия подстановочного знака.

Другим способом сравнения будет:

(newType == (Class<? extends Object[]>)Object[].class)

В чем разница между (T[]) new Object[newLength] и (T[]) Array.newInstance(newType.getComponentType(), newLength)?

  • new Object[...] создает массив обычным способом, типа, который статически известен. Помните, что код только что проверил, что T[] есть Object[].
  • Array.newInstance(...) использует отражение для динамического создания массива типа Class, переданного в.

Почему Array.newInstance недостаточно хорош для обоих случаев?

Операция с использованием отражения в целом медленнее, чем ее неотражающая копия.

руководство по размышлению говорит:

Поскольку отражение включает типы, которые динамически разрешены, некоторые оптимизации виртуальной машины Java не могут быть выполнены. Следовательно, рефлексивные операции имеют более низкую производительность, чем их неотражающие аналоги, и их следует избегать в разделах кода, которые часто вызываются в приложениях с высокой чувствительностью.

Java SE заполняется такой микро-оптимизацией. Авторы SE пытаются выжать все, что могут, из него.

Но я бы не стал беспокоиться о хите производительности в этом случае: newInstance и copyOf Внутренние свойства HotSpot. Это означает, что в идеале призывы к этим методам заменяются машинной сборкой. Анекдотически я провел несколько тестов и нашел разницу между new Object[...] и Array.newInstance(...) незначительной. Код в вопросе, вероятно, является реликвией, хотя он может быть полезен и для менее хорошо оборудованных JVM.

Отражение также может быть отключено в определенных контекстах со строгой безопасностью (например, апплет), но обычно не для обычного рабочего приложения.

Когда я должен использовать этот метод?

В общем, вы, вероятно, никогда не будете использовать эту перегрузку. Эта перегрузка полезна, если вы хотите изменить тип массива.

  • Widening:

    Object[] a = Arrays.copyOf(
        new String[] { "hello", "world" }, 3, Object[].class);
    a[2] = Character.valueOf('!');
    System.out.println(Arrays.toString(a));
    
  • Сужение:

    String[] a = Arrays.copyOf(
        new Object[] { "hello", "world" }, 2, String[].class);
    System.out.println(String.join(" ", a));
    

Более типично использовать Arrays.copyOf(T[], int).

Ответ 2

  • Что это за проверка строки?
(Object)newType == (Object)Object[].class

Проверяет, содержит ли переменная newType ссылку на экземпляр java.lang.Class, представляющий тип Object[]. Броски не используются.

  1. В чем разница между (T[]) new Object[newLength] и (T[]) Array.newInstance(newType.getComponentType(), newLength). почему Array.newInstance недостаточно для обоих случаев?

Насколько я могу судить, Array.newInstance() может использоваться в обоих случаях, но нерелятивистская конструкция обычного массива, скорее всего, немного быстрее. Таким образом, я полагаю, что Object[] вызывается как частный случай по соображениям производительности, но я понятия не имею, достаточно ли этот случай используется для оптимизации, чтобы быть важным.

  1. Эта следующая строка компилируется, но сбой во время выполнения (как и ожидалось). Когда следует использовать этот метод?
Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 

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

Ответ 3

  • Проверяет, является ли newType массивом объектов или нет:

    Object[] a1 = new Object[100]; -- array of Objects
    
    String[] a2 = new String[100]; -- array of Strings
    

Зачем это делать? Поскольку новый Object [n] быстрее, чем Array.newInstance

  1. Array.newInstance(Class<?> componentType, int... dimensions) создает массив типов, определяемый первым аргументом, например String.classString[]. Обратите внимание, что String[].class.getComponentType() возвращает String.class

  2. Вы не можете использовать его так, но это может быть как

    Integer[] nums = Arrays.copyOf(new Object[]{1, 2}, 2, Integer[].class);
    

в этом случае зависит только от фактического типа элементов, например

  Arrays.copyOf(new Object[]{1L, 2}, 2, Integer[].class);

не удастся, вы не можете записать в Integer[] ничего, кроме Integer

Ответ 4

Прежде всего, листинг в этой строке

((Object)newType == (Object)Object[].class)

абсолютно необходимы. Удаление их приведет к ошибке компиляции:

incomparable types: Class<CAP#1> and Class<Object[]>
 where CAP#1 is a fresh type-variable:
  CAP#1 extends T[] from capture of ? extends T[]

Теперь, чтобы ответить на ваш вопрос Что это за проверка строки?

Он просто проверяет, является ли данный массив типом объекта, который является частью ответа для вашего другого вопроса Почему Array.newInstance недостаточно хорош для обоих случаев?

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

Что касается вашего последнего примера,

Integer[] nums = Arrays.copyOf(new String[]{"a", "b"}, 2, Integer[].class) 

Что он скомпилирует, это правда. Потому что данные аргументы метода - это все валиды. Это, безусловно, не удастся во время выполнения; каков будет ожидаемый результат преобразования типа "a" в Integer?

Теперь, когда использовать copyOf? Когда вы уже знаете оба типа, и уже знаете, что они действительны вместе.

Основное использование заключается в возврате копии, но усеченной или дополненной [null/default values] исходным массивом.

Ответ 5

Позвольте мне попытаться ответить на этот вопрос:

Чтобы ответить на ваш первый вопрос, он проверяет, совпадает ли тип newType с типами в массиве. Оба они также повышают уровень типов до типа объекта. То есть, он пытается увидеть, является ли родительский тип массива объектом. Смотрите этот вопрос SO на тему "Ускорение и подавление" . Я предполагаю, что он бросает не потому, что проверяет безопасность типа. Хотя все объекты в Java выводятся из объектов как суперкласс.

Было бы полезно заметить, что

 T[] copy = ((Object)newType == (Object)Object[].class)
    ? (T[]) new Object[newLength]
    : (T[]) Array.newInstance(newType.getComponentType(), newLength);

- фактически одна строка. т.е. это условно условное условие if-else.

result = (condition) ? (doThisIfTrue) : (elseDoThisIfFalse)

Простой пример здесь

Итак, по существу, эта строка будет такой же, как:

T[] copy;
Boolean condition = ((Object)newType == (Object)Object[].class)
if(condition) 
     copy = (T[]) new Object[newLength];
else 
     copy = (T[]) Array.newInstance(newType.getComponentType(), newLength);

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

Arrays.copyOf создаст новый массив (со ссылками на старый), но с более новой длиной и проложит неиспользуемые позиции с пустыми объектами. Это то, что он делает в массиве ints, в котором он заполняет неиспользуемые индексы нулями.

Arrays.CopyOf служит для предоставления мелкой копии объектов, то есть относится к старым элементам, но в новом массиве. Этот вопрос SO имеет более подробную информацию об этом.