Najszybszy sposób na ustawienie wszystkich wartości tablicy?


Mam
char []
i chcę ustawić wartość każdego indeksu na tę samą wartość
char
.

Jest na to oczywisty sposób (iteracja):
char f = '+';
char [] c = new char [50];
for(int i = 0; i < c.length; i++){
c[i] = f;
}

Ale zastanawiałem się, czy istnieje sposób, w jaki mogę użyć
System.arraycopy
lub czegoś równoważnego, który obejdzie potrzebę iteracji. Czy jest na to sposób?

EDIT :

autor:
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;
}

To jest dokładnie ten sam proces, który pokazuje, że może nie być lepszego sposobu na zrobienie tego.

+1 dla wszystkich, którzy mimo wszystko zasugerowali
wypełnij
- wszystko w porządku i dzięki.
Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Wypróbuj
Arrays.fill (c, f)
:

tablice dokumentacji
http://docs.oracle.com/javase/ ... %255D,%20char%29
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jako kolejna opcja i dla potomnych, ostatnio przyjrzałem się temu i znalazłem rozwiązanie, które pozwala znacznie skrócić pętlę, przekazując część pracy klasie

System
http://docs.oracle.com/javase/ ... .html
które (jeśli używana maszyna JVM jest wystarczająco inteligentna) może zostać przekształcone w operację

memset
https://coderoad.ru/7367677/
:-
/*
* 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);
}
}

Ta decyzja została podjęta na podstawie badań IBM

„Wydajność serwera Java: wydajne budowanie, skalowalne Jvms” autorstwa R. Dimpsy, R. Arora, K. Kuiper
https://ieeexplore.ieee.org/document/5387064
.

Uproszczone wyjaśnienie

Jak sugeruje komentarz, ustawia to indeks 0 tablicy docelowej na twoją wartość, a następnie używa

systemowy
http://docs.oracle.com/javase/ ... .html
klasa do kopiowania jednego obiektu, czyli obiekt o indeksie 0 do indeksu 1, następnie te dwa obiekty (indeks 0 i 1) do 2 i 3, a następnie te cztery obiekty (0,1,2 i 3) do 4, 5,6 i 7 i tak dalej ...

Wydajność (w momencie pisania

artykuły)
W szybkim biegu, chwytając
System.nanoTime ()
przed i po i obliczając czas trwania, który wymyśliłem: -
  • Ta metoda: 332,617 - 390,262 („najwyższy - najniższy” z 10 testów)
  • Float[] n = new Float[array.length];//Fill with null
    : 666,650
  • Ustawienie pętli: 3,743,488 - 9,767,744 („najwyższy - najniższy” z 10 testów)
  • Arrays.fill
    : 12,539,336


Kompilacja JVM i JIT

Należy zauważyć, że wraz z ewolucją JVM i JIT podejście to może stać się przestarzałe, ponieważ optymalizacje bibliotek i środowiska wykonawczego mogą osiągnąć lub nawet przekroczyć te liczby, używając po prostu
fill ()
.
W chwili pisania tego tekstu była to najszybsza opcja, jaką znalazłem. Wspomniano już, że może tak nie być w tej chwili, ale nie testowałem. To jest piękno i przekleństwo Javy.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Użyj
Arrays.fill
char f = '+';
char [] c = new char [50];
Arrays.fill(c, f)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Często zadawane pytania programistów Java Część w sekcji 6
http://www.ensta-paristech.fr/ ... .html
oferuje:
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);
}

To zasadniczo powoduje wywołanie log2 (array.length) do System.arraycopy, który, miejmy nadzieję, używa zoptymalizowanej implementacji memcpy.
Jednak czy ta technika jest nadal wymagana w nowoczesnych JIT Java, takich jak Oracle/Android JIT?
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

System.arraycopy to moja odpowiedź. Daj mi znać, jeśli są jakieś lepsze sposoby. Tnx
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.
*/
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

Dwukrotne przetwarzanie macierzy zajmuje 133536 nanosekund.

Cykl system.arraycopy trwa 22070 nano sekund.

Jedna runda Arrays.fill zajmuje 9777 nano sekund.

Dwie rundy wypełnienia Arrays.fill zajmą 93 028 nano sekund.





12:33:38

Dwuetapowe przetwarzanie tablic zajmuje 133816 nanosekund.

Cykl system.arraycopy trwa 22070 nano sekund.

Jedna runda Arrays.fill zajmuje 17042 nano sekund.

Dwie rundy Arrays.fill zajmą 95263 nano sekund.





12:33:51

Dwuetapowe przetwarzanie macierzy zajmuje 199187 nanosekund.

Cykl system.arraycopy trwa 44140 nano sekund.

Jedna runda Arrays.fill zajmuje 19555 nano sekund.

Dwie rundy wypełnienia Arrays.fill zajmą 449219 nano sekund.





12:34:16

Dwukrotne przetwarzanie macierzy zajmuje 199467 nanosekund.

Cykl Arraycopy System. trwa 42464 nano sekund.

Jedna runda Arrays.fill zajmuje 17600 nano sekund.

Dwie rundy Arrays.fill zajmą 170 971 nano sekund.





12:34:26

Dwuetapowe przetwarzanie macierzy zajmuje 198907 nanosekund.

Cykl Arraycopy System trwa 24584 nano sekund.

Jedna runda Arrays.fill zajmuje 10616 nano sekund.

Dwie rundy Arrays.fill zajmują 94,426 nano sekund.



Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli masz inną tablicę znaków,
char [] b
, i chcesz zamienić
c
na
b
, możesz użyć
c = b.clone ();
.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Od wersji Java-8 są cztery

opcja
https://docs.oracle.com/javase ... .html
setAll, która ustawia wszystkie elementy określonej tablicy przy użyciu podanej funkcji generatora do obliczenia każdego elementu.
Tylko z tych czterech przeciążeń

trzy

z nich przyjmują tablicę prymitywów, zadeklarowanych jako takie:



Przykłady wykorzystania powyższych metod:
// 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);

Funkcja dostarczona do metody
setAll
pobiera indeks elementu i zwraca wartość dla tego indeksu.
możesz się zastanawiać, co z tablicą znaków?
Tutaj pojawia się czwarte przeciążenie metody
setAll
. Ponieważ nie ma przeciążenia, które zużywa tablicę prymitywów znaków, jedyną opcją, jaką mamy, jest zmiana deklaracji tablicy znaków na typ
Character []
.
Jeśli zmiana typu tablicy na
Character
nie jest właściwa, możesz powrócić do metody Arrays.fill.
Przykład użycia metody
setAll
z
Character []
:
// given an index, set the element at the specified index with the provided value
Character[] character = new Character[50];
Arrays.setAll(characters, index -> '+');

Chociaż dla

ustawienie pewnego

wartości łatwiej jest użyć metody
Arrays.fill
niż metody
setAll
.
Metoda
setAll
ma tę zaletę, że możesz ustawić wszystkie elementy tablicy na tę samą wartość lub wygenerować tablicę liczb parzystych, nieparzystych lub dowolną inną formułę:
dawny.
int[] evenNumbers = new int[10]; 
Arrays.setAll(evenNumbers, i -> i * 2);

Istnieje również kilka przeciążeń metody parallelSetAll, które są wykonywane równolegle, chociaż należy zauważyć, że funkcja przekazana do metody parallelSetAll jest

musi być wolny od skutków ubocznych

.

Wynik

Jeśli Twoim celem jest po prostu

ustalić pewne

value dla każdego elementu tablicy, wtedy użycie przeciążeń
Arrays.fill
byłoby najbardziej odpowiednią opcją. Jeśli jednak chcesz być bardziej elastyczny lub generować elementy na żądanie, najlepiej jest użyć
Arrays.setAll
lub
Arrays.parallelSetAll
(w stosownych przypadkach).
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Cm

.
http://docs.oracle.com/javase/ ... .html
Metoda Arrays.fill:
char f = '+';
char [] c = new char [50];
Arrays.fill(c, f);
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

array.Fill może zaspokoić swoje potrzeby
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Arrays.fill(myArray, 'c');
Arrays.fill
http://docs.oracle.com/javase/ ... %255D,%20char%29
Chociaż jest całkowicie możliwe, że robi to pętlę w tle, a zatem nie jest bardziej wydajne niż to, co masz (poza zapisywaniem linii kodu). Jeśli naprawdę zależy Ci na wydajności, wypróbuj poniższe W porównaniu z powyższym:
int size = 50;
char[] array = new char[size];
for (int i=0; i<size; i++){
array[i] = 'c';
}

Zauważ, że powyższe nie wywołuje funkcji array.size () dla każdej iteracji.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

/**
* 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;
}

W ten sposób robi to Arrays.fill.
(Przypuszczam, że możesz zajrzeć do JNI i użyć
memset
.)
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Mam niewielką poprawę odpowiedzi Rossa Drew.
Dla

mały

prosta pętla tablicy jest szybsza niż metoda System.arraycopy ze względu na obciążenie związane z konfiguracją System.arraycopy. Dlatego lepiej jest wypełnić kilka pierwszych bajtów tablicy prostą pętlą i przechodzić do System.arraycopy tylko wtedy, gdy wypełniona tablica ma określony rozmiar.
Optymalny rozmiar początkowej pętli będzie zależał od maszyny JVM i oczywiście od konkretnego systemu.
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);
}
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Możesz użyć
arraycopy
, ale zależy to od tego, czy możesz wcześniej zdefiniować oryginalną tablicę - czy za każdym razem musisz wypełniać różne znaki, czy też wypełnisz tablice wielokrotnie tym samym znakiem?
Oczywiście długość dopełnienia ma znaczenie - albo potrzebujesz źródła, które jest większe niż wszystkie możliwe miejsca docelowe, albo potrzebujesz pętli do wielokrotnego kopiowania w szyku porcji danych, aż miejsce docelowe będzie pełne.
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);
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Arrays.fill to najlepsza opcja do zastosowań ogólnych.
Jeśli musisz zapełniać duże tablice, chociaż od najnowszego idk 1.8 u102, istnieje szybszy sposób, w którym używa się System.arraycopy.
Możesz przyjrzeć się tej alternatywnej implementacji Arrays.fill:
Według

wzorce
https://github.com/zolyfarkas/ ... .java
JMH, możesz uzyskać prawie dwukrotny wzrost wydajności w przypadku dużych macierzy (1000+)
W każdym razie te implementacje powinny być używane tylko w razie potrzeby. JDKs Arrays.fill powinien być preferowanym wyborem.

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się