IT Share you

배열의 모든 값을 설정하는 가장 빠른 방법은 무엇입니까?

shareyou 2020. 11. 29. 12:35
반응형

배열의 모든 값을 설정하는 가장 빠른 방법은 무엇입니까?


나는을 가지고 있으며 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모두 정확하고 감사합니다.


시도 Arrays.fill(c, f): 배열 javadoc


또 다른 옵션이자 후손을 위해 나는 최근에 이것을 조사하고 있었고 일부 작업을 System 클래스 에 넘겨 훨씬 더 짧은 루프를 허용하는 솔루션을 제공하는 기사를 찾았습니다. (사용중인 JVM이 충분히 똑똑한 경우 )는 memset 작업 으로 바뀔 수 있습니다 .

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

이 솔루션은 R. Dimpsey, R. Arora, K. Kuiper 의 IBM 연구 논문 "Java 서버 성능 : 효율적이고 확장 가능한 Jvm 구축 사례 연구"에서 발췌했습니다 .

간단한 설명

주석에서 알 수 있듯이 대상 배열의 인덱스 0을 값으로 설정 한 다음 System 클래스를 사용하여 하나의 객체, 즉 인덱스 0의 객체를 인덱스 1로 복사 한 다음이 두 객체 (인덱스 0 및 1)를 2와 3으로 복사 한 다음 4 개의 객체 (0,1,2, 3)를 4,5,6, 7 등으로 ...

효율성 (작성 시점)

빠른 실행에서 System.nanoTime()이전과 이후를 잡고 내가 생각 해낸 기간을 계산합니다.

  • 이 방법 : 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(). 글을 쓰는 시점에서 이것은 내가 찾은 가장 빠른 옵션이었습니다. 지금은 그렇지 않을 수도 있다고 언급되었지만 확인하지 않았습니다. 이것이 자바의 아름다움과 저주입니다.


사용하다 Arrays.fill

  char f = '+';
  char [] c = new char [50];
  Arrays.fill(c, f)

자바 프로그래머의 FAQ 파트 B 섹션 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);
}

이것은 본질적으로 최적화 된 memcpy 구현을 활용하는 System.arraycopy에 대한 log2 (array.length) 호출을 만듭니다.

그러나이 기술은 Oracle / Android JIT와 같은 최신 Java JIT에 여전히 필요합니까?


System.arraycopy가 제 대답입니다. 더 좋은 방법이 있는지 알려주세요. 고마워

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
Arrays의 2- 루핑에는 133536 나노초가 걸립니다.
System.arraycopy 루핑은 22070 나노초가 걸립니다.
1 라운드 Arrays.fill은 9777 나노초가 걸립니다.
두 라운드 Arrays.fill은 93028 나노초가 걸립니다.

12:33:38
Arrays의 2- 루핑에는 133816 나노초가 걸립니다.
System.arraycopy 루핑은 22070 나노초가 걸립니다.
하나의 라운드 Arrays.fill은 17042 나노초가 걸립니다.
두 라운드 Arrays.fill은 95263 나노초가 걸립니다.

12:33:51
Arrays의 2- 루핑은 199187 나노초가 걸립니다.
System.arraycopy 루핑은 44140 나노초가 걸립니다.
1 라운드 Arrays.fill은 19555 나노초가 걸립니다.
두 라운드 Arrays.fill은 449219 나노초가 걸립니다.

12:34:16
Arrays의 2- 루핑은 199467 나노초가 걸립니다.
System.arraycopy 루핑은 42464 나노초가 걸립니다.
하나의 라운드 Arrays.fill은 17600 나노초가 걸립니다.
두 라운드 Arrays.fill은 170971 나노초가 걸립니다.

12:34:26
Arrays의 2- 루핑에는 198907 나노초가 걸립니다.
System.arraycopy 루핑은 24584 나노초가 걸립니다.
한 라운드 Arrays.fill은 10616 나노초가 걸립니다.
두 라운드 Arrays.fill은 94426 나노초가 걸립니다.


Arrays.fill 메서드를 참조하십시오 .

char f = '+';
char [] c = new char [50];
Arrays.fill(c, f);

당신은 문자의 다른 배열을 가지고있는 경우 char[] b와 교체 할 c과 함께 b, 당신은 사용할 수 있습니다 c=b.clone();.


Arrays.fill(myArray, 'c');

Arrays.fill

Although it is quite possible that this is doing the loop in the background and is therefore not any more efficient than what you have (other than the lines of code savings). If you really care about efficiency, try the following in comparison to the above:

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

Notice that the above doesn't call array.size() for each iteration.


Arrays.fill might suit your needs


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

That's the way Arrays.fill does it.

(I suppose you could drop into JNI and use memset.)


As of Java-8, there are four variants of the setAll method which sets all elements of the specified array, using a provided generator function to compute each element.

Of those four overloads only three of them accept an array of primitives declared as such:





Examples of how to use the aforementioned methods:

// 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);

The function provided to the setAll method receives the element index and returns a value for that index.

you may be wondering how about characters array?

This is where the fourth overload of the setAll method comes into play. As there is no overload that consumes an array of character primitives, the only option we have is to change the declaration of our character array to a type Character[].

If changing the type of the array to Character is not appropriate then you can fall back to the Arrays.fill method.

Example of using the setAll method with Character[]:

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

Although, it's simpler to use the Arrays.fill method rather than the setAll method to set a specific value.

The setAll method has the advantage that you can either set all the elements of the array to have the same value or generate an array of even numbers, odd numbers or any other formula:

e.g.

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

There's also several overloads of the parallelSetAll method which is executed in parallel, although it's important to note that the function passed to the parallelSetAll method must be side-effect free.

Conclusion

If your goal is simply to set a specific value for each element of the array then using the Arrays.fill overloads would be the most appropriate option. However, if you want to be more flexible or generate elements on demand then using the Arrays.setAll or Arrays.parallelSetAll (when appropriate) would be the option to go for.


You could use arraycopy but it depends on whether you can predefine the source array, - do you need a different character fill each time, or are you filling arrays repeatedly with the same char?

Clearly the length of the fill matters - either you need a source that is bigger than all possible destinations, or you need a loop to repeatedly arraycopy a chunk of data until the destination is full.

    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);

Arrays.fill is the best option for general purpose use. If you need to fill large arrays though as of latest idk 1.8 u102, there is a faster way that leverages System.arraycopy. You can take a look at this alternate Arrays.fill implementation:

According to the JMH benchmarks you can get almost 2x performance boost for large arrays (1000 +)

In any case, these implementations should be used only where needed. JDKs Arrays.fill should be the preferred choice.


I have a minor improvement on Ross Drew's answer.

For a small array, a simple loop is faster than the System.arraycopy approach, because of the overhead associated with setting up System.arraycopy. Therefore, it's better to fill the first few bytes of the array using a simple loop, and only move to System.arraycopy when the filled array has a certain size.

The optimal size of the initial loop will be JVM specific and system specific of course.

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);
  }
}

참고URL : https://stackoverflow.com/questions/9128737/fastest-way-to-set-all-values-of-an-array

반응형