Самый быстрый способ установить все значения массива?

У меня есть a char [], и я хочу установить значение каждого индекса к тому же char значение.
Существует очевидный способ сделать это (повторение):

  char f = '+';
  char [] c = new char [50];
  for(int i = 0; i < c.length; i++){
      c[i] = f;
  }

Но я задавался вопросом, существует ли способ, которым я могу использовать System.arraycopy или что-то эквивалентное, которое обошло бы потребность выполнить итерации. Существует ли способ сделать это?

РЕДАКТИРОВАНИЕ: от Arrays.java

public static void fill(char[] a, int fromIndex, int toIndex, char val) {
        rangeCheck(a.length, fromIndex, toIndex);
        for (int i = fromIndex; i < toIndex; i++)
            a[i] = val;
    }

Это - точно тот же процесс, который показывает, что не могло бы быть лучшего способа сделать это.
+1 всем, кто предложил fill так или иначе - Вы все корректны и спасибо.

62
задан 3 February 2012 в 16:41

14 ответов

Попробуйте Arrays.fill(c, f): javadoc

Массивов
86
ответ дан 31 October 2019 в 13:27

Как другая опция и для потомства я изучал это недавно и нашел этот статья, которая предлагает решение, которое позволяет намного более короткий цикл путем передавания части работы к Система класс, который (если JVM Вы используете, достаточно умно) может быть превращен в memset operation:-

/*
 * initialize a smaller piece of the array and use the System.arraycopy 
 * call to fill in the rest of the array in an expanding binary fashion
 */
public static void bytefill(byte[] array, byte value) {
  int len = array.length;

  if (len > 0){
    array[0] = value;
  }

  //Value of i will be [1, 2, 4, 8, 16, 32, ..., len]
  for (int i = 1; i < len; i += i) {
    System.arraycopy(array, 0, array, i, ((len - i) < i) ? (len - i) : i);
  }
}

, Это решение было взято от научно-исследовательской работы IBM "производительность сервера Java: тематическое исследование создания эффективного, масштабируемого Jvms" R. Dimpsey, R. Arora, K. Kuiper.

Упрощенное объяснение

Как комментарий предлагает, это устанавливает индекс 0 целевого массива к Вашему значению, затем использует Система класс для копирования одного объекта т.е. объекта в индексе 0 к индексу 1 затем те два объекта (индекс 0 и 1) в 2 и 3, затем те четыре объекта (0,1,2 и 3) в 4,5,6 и 7 и так далее...

Эффективность (при записи)

В быстром пробегает, захват System.nanoTime() прежде и после и вычисление продолжительности, я подошел with:-

  • Этот метод: 332 617 - 390 262 ('самый высокий - самый низкий' от 10 тестов)
  • Float[] n = new Float[array.length]; //Fill with null: 666,650
  • Установка через цикл: 3,743,488 - 9,767,744 ('самый высокий - самый низкий' от 10 тестов)
  • Arrays.fill: 12,539,336

JVM и JIT-компиляция

нужно отметить, что как JVM и JIT развивается, этот подход может стать устаревшим как библиотека, и оптимизации во время выполнения могли достигнуть или даже превысить эти числа просто с помощью fill(). Во время записи это было самой быстрой опцией, которую я нашел. Было упомянуто, что это не могло бы иметь место теперь, но я не проверил. Это - красота и проклятие Java.

40
ответ дан 31 October 2019 в 13:27

Программист Java Секта Части B FAQ 6 предлагает:

public static void bytefill(byte[] array, byte value) {
    int len = array.length;
    if (len > 0)
    array[0] = value;
    for (int i = 1; i < len; i += i)
        System.arraycopy( array, 0, array, i,
            ((len - i) < i) ? (len - i) : i);
}

Это по существу делает log2 (array.length), звонит в System.arraycopy, который, надо надеяться, использует оптимизированную memcpy реализацию.

Однако эта техника, все еще требуемая на современных МОНЕТАХ В ПЯТЬ ЦЕНТОВ Java, таких как JIT Oracle/Android?

6
ответ дан 31 October 2019 в 13:27

System.arraycopy является моим ответом. Сообщенный мне там любые лучшие пути. Thanks

private static long[] r1 = new long[64];
private static long[][] r2 = new long[64][64];

/**Proved:
 * {@link Arrays#fill(long[], long[])} makes r2 has 64 references to r1 - not the answer;
 * {@link Arrays#fill(long[], long)} sometimes slower than deep 2 looping.<br/>
 */
private static void testFillPerformance() {
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    System.out.println(sdf.format(new Date()));
    Arrays.fill(r1, 0l);

    long stamp0 = System.nanoTime();
    //      Arrays.fill(r2, 0l); -- exception
    long stamp1 = System.nanoTime();
    //      System.out.println(String.format("Arrays.fill takes %s nano-seconds.", stamp1 - stamp0));

    stamp0 = System.nanoTime();
    for (int i = 0; i < 64; i++) {
        for (int j = 0; j < 64; j++)
            r2[i][j] = 0l;
    }
    stamp1 = System.nanoTime();
    System.out.println(String.format("Arrays' 2-looping takes %s nano-seconds.", stamp1 - stamp0));

    stamp0 = System.nanoTime();
    for (int i = 0; i < 64; i++) {
        System.arraycopy(r1, 0, r2[i], 0, 64);
    }
    stamp1 = System.nanoTime();
    System.out.println(String.format("System.arraycopy looping takes %s nano-seconds.", stamp1 - stamp0));

    stamp0 = System.nanoTime();
    Arrays.fill(r2, r1);
    stamp1 = System.nanoTime();
    System.out.println(String.format("One round Arrays.fill takes %s nano-seconds.", stamp1 - stamp0));

    stamp0 = System.nanoTime();
    for (int i = 0; i < 64; i++)
        Arrays.fill(r2[i], 0l);
    stamp1 = System.nanoTime();
    System.out.println(String.format("Two rounds Arrays.fill takes %s nano-seconds.", stamp1 - stamp0));
}

12:33:18
взятия Массивов с 2 цикличными выполнениями 133 536 наносекунд.
цикличное выполнение System.arraycopy занимает 22 070 наносекунд.
Один раунд Arrays.fill занимает 9 777 наносекунд.
Два раунда Arrays.fill занимает 93 028 наносекунд.

12:33:38
взятия Массивов с 2 цикличными выполнениями 133 816 наносекунд.
цикличное выполнение System.arraycopy занимает 22 070 наносекунд.
Один раунд Arrays.fill занимает 17 042 наносекунды.
Два раунда Arrays.fill занимает 95 263 наносекунды.

12:33:51
взятия Массивов с 2 цикличными выполнениями 199 187 наносекунд.
цикличное выполнение System.arraycopy занимает 44 140 наносекунд.
Один раунд Arrays.fill занимает 19 555 наносекунд.
Два раунда Arrays.fill занимает 449 219 наносекунд.

12:34:16
взятия Массивов с 2 цикличными выполнениями 199 467 наносекунд.
цикличное выполнение System.arraycopy занимает 42 464 наносекунды.
Один раунд Arrays.fill занимает 17 600 наносекунд.
Два раунда Arrays.fill занимает 170 971 наносекунду.

12:34:26
взятия Массивов с 2 цикличными выполнениями 198 907 наносекунд.
цикличное выполнение System.arraycopy занимает 24 584 наносекунды.
Один раунд Arrays.fill занимает 10 616 наносекунд.
Два раунда Arrays.fill занимает 94 426 наносекунд.

6
ответ дан 31 October 2019 в 13:27

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

Из тех четырех перегрузок только [1 122] три из них принимают массив примитивов, объявленных как таковыми:

<час> <час> <час> <час>

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

// given an index, set the element at the specified index with the provided value
double [] doubles = new double[50];
Arrays.setAll(doubles, index -> 30D);

// given an index, set the element at the specified index with the provided value
int [] ints = new int[50];
Arrays.setAll(ints, index -> 60);

 // given an index, set the element at the specified index with the provided value
long [] longs = new long[50];
Arrays.setAll(longs, index -> 90L);

функция, предоставленная setAll, метод получает индекс элемента и возвращает значение для того индекса.

можно ли задаваться вопросом как насчет массива символов?

Это - то, где четвертая перегрузка setAll метод играет роль. Как нет никакой перегрузки, которая использует массив символьных примитивов, единственная опция, которую мы имеем, состоит в том, чтобы изменить объявление нашего символьного массива к типу Character[].

, Если изменение типа массива к Character не является соответствующим затем, можно отступить к метод Arrays.fill .

Пример использования setAll метод с Character[]:

// given an index, set the element at the specified index with the provided value
Character[] character = new Character[50];
Arrays.setAll(characters, index -> '+'); 

, Хотя, более просто использовать Arrays.fill метод, а не setAll, метод к [1 123] установил определенное значение.

setAll метод имеет преимущество, что можно или установить все элементы массива, чтобы иметь то же значение или генерировать массив четных чисел, нечетных чисел или любой другой формулы:

, например,

int[] evenNumbers = new int[10]; 
Arrays.setAll(evenNumbers, i -> i * 2);

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

Заключение

, Если Ваша цель просто для установки определенного значение для каждого элемента массива затем с помощью эти Arrays.fill перегрузки, было бы наиболее подходящим вариантом. Однако, если бы Вы хотите быть более гибкими или генерировать элементы по требованию затем, использование Arrays.setAll или Arrays.parallelSetAll (когда соответствующий) было бы опцией пойти для.

1
ответ дан 31 October 2019 в 13:27

Arrays.fill является наилучшим вариантом для использования общего назначения. Если необходимо заполнить большие массивы хотя с последнего idk 1.8 u102, существует более быстрый путь, который усиливает System.arraycopy. Можно смотреть на эту альтернативу реализация Arrays.fill :

Согласно сравнительные тесты JMH можно добраться почти 2x повышение производительности для больших массивов (1000 +)

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

0
ответ дан 31 October 2019 в 13:27

У меня есть незначительное улучшение на ответе Ross Drew.

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

оптимальным размером начального цикла будет конкретная JVM и система, конкретная, конечно.

private static final int SMALL = 16;

public static void arrayFill(byte[] array, byte value) {
  int len = array.length;
  int lenB = len < SMALL ? len : SMALL;

  for (int i = 0; i < lenB; i++) {
    array[i] = value;
  }

  for (int i = SMALL; i < len; i += i) {
    System.arraycopy(array, 0, array, i, len < i + i ? len - i : i);
  }
}
0
ответ дан 31 October 2019 в 13:27

Используйте Arrays.fill

  char f = '+';
  char [] c = new char [50];
  Arrays.fill(c, f)
11
ответ дан 31 October 2019 в 13:27

См. метод Arrays.fill :

char f = '+';
char [] c = new char [50];
Arrays.fill(c, f);
3
ответ дан 31 October 2019 в 13:27

Если у Вас есть другой массив символа, char[] b, и Вы хотите заменить c b, можно использовать c=b.clone();.

3
ответ дан 31 October 2019 в 13:27

Arrays.fill(myArray, 'c');

Arrays.fill

, Хотя довольно возможно, что это делает цикл в фоновом режиме и поэтому не больше эффективно, чем, что Вы имеете (кроме строк сбережений кода). Если Вы действительно заботитесь об эффективности, попробуйте следующее по сравнению с вышеупомянутым:

int size = 50;
char[] array = new char[size];
for (int i=0; i<size; i++){
  array[i] = 'c';
}

Уведомление, что вышеупомянутое не называет array.size () для каждого повторения.

2
ответ дан 31 October 2019 в 13:27

Arrays.fill мог бы удовлетворить Вашим потребностям

1
ответ дан 31 October 2019 в 13:27
   /**
     * Assigns the specified char value to each element of the specified array
     * of chars.
     *
     * @param a the array to be filled
     * @param val the value to be stored in all elements of the array
     */
    public static void fill(char[] a, char val) {
        for (int i = 0, len = a.length; i < len; i++)
            a[i] = val;
    }

Это - способ, которым Arrays.fill делает это.

(я предполагаю, Вы могли заскочить в JNI и использование memset.)

1
ответ дан 31 October 2019 в 13:27

Вы могли использовать arraycopy, но это зависит от того, можно ли предопределить исходный массив, - Вам нужна другая символьная заливка каждый раз, или Вы заполняете массивы неоднократно тем же символом?

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

    char f = '+';
    char[] c = new char[50];
    for (int i = 0; i < c.length; i++)
    {
        c[i] = f;
    }

    char[] d = new char[50];
    System.arraycopy(c, 0, d, 0, d.length);
0
ответ дан 31 October 2019 в 13:27

Другие вопросы по тегам:

Похожие вопросы: