94

I have a char [], and I want to set the value of every index to the same char value.
There is the obvious way to do it (iteration):

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

But I was wondering if there's a way that I can utilize System.arraycopy or something equivalent that would bypass the need to iterate. Is there a way to do that?

EDIT : From 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;
    }

This is exactly the same process, which shows that there might not be a better way to do this.
+1 to everyone who suggested fill anyway - you're all correct and thank you.

rtheunissen
  • 7,347
  • 5
  • 34
  • 65
  • 2
    The version of the JDK code in your addendum shows a "swizzle" that is done in some versions of the JDK: An external flag somewhere indicates that array bounds checking should be bypassed in the method, and then an explicit bounds check is added, outside the loop. This provides a significant performance boost, since bounds checking is not only expensive in its own right, but it complicates other optimizations. – Hot Licks Feb 03 '12 at 17:19
  • @Bombe it's for a custom password field, so I have to replace every `char` in the document with `'•'` on the fly - which means it has to be as responsive as possible. Might say why not set value for each index as you go? It's for using with `drawString`, so I can anti-alias the •'s text. `fill` seems to work well. :) – rtheunissen Feb 04 '12 at 00:28
  • @paranoid-android, so you do have users that are able to type more than 1000 characters per second? I am impressed. – Bombe Feb 04 '12 at 02:14
  • 3
    Yeah, I'm coding for Superman. – rtheunissen Feb 04 '12 at 02:21
  • I think it is better to use 'fill' just because it is a standard method of built-in class and could be changed to more effective implementation by JVM at runtime. Even more i think it is bound to happen. Java doesn't support direct memory access by design, but that doesn't mean that generated code shouldn't support it too. – weaknespase Aug 16 '14 at 09:29

14 Answers14

114

Try Arrays.fill(c, f) : Arrays javadoc

Kurt Du Bois
  • 7,550
  • 4
  • 25
  • 33
68

As another option and for posterity I was looking into this recently and found a solution that allows a much shorter loop by handing some of the work off to the System class, which (if the JVM you're using is smart enough) can be turned into a 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);
  }
}

This solution was taken from the IBM research paper "Java server performance: A case study of building efficient, scalable Jvms" by R. Dimpsey, R. Arora, K. Kuiper.

Simplified explanation

As the comment suggests, this sets index 0 of the destination array to your value then uses the System class to copy one object i.e. the object at index 0 to index 1 then those two objects (index 0 and 1) into 2 and 3, then those four objects (0,1,2 and 3) into 4,5,6 and 7 and so on...

Efficiency (at the point of writing)

In a quick run through, grabbing the System.nanoTime() before and after and calculating a duration I came up with:-

  • This method : 332,617 - 390,262 ('highest - lowest' from 10 tests)
  • Float[] n = new Float[array.length]; //Fill with null : 666,650
  • Setting via loop : 3,743,488 - 9,767,744 ('highest - lowest' from 10 tests)
  • Arrays.fill : 12,539,336

The JVM and JIT compilation

It should be noted that as the JVM and JIT evolves, this approach may well become obsolete as library and runtime optimisations could reach or even exceed these numbers simply using fill(). At the time of writing, this was the fastest option I had found. It has been mentioned this might not be the case now but I have not checked. This is the beauty and the curse of Java.

Community
  • 1
  • 1
Ross Drew
  • 8,163
  • 2
  • 41
  • 53
  • 3
    This shall be the accepted answer, Arrays.fill is too slow for large arrays when you are looking for speed. OP states "fastest way". This answer really helps – M.E. Nov 22 '19 at 18:53
  • Isn't this basically what Bill Tutt already posted? – timbo Dec 24 '21 at 19:54
  • 1
    @timbo in that it's the same approach yes. I posted my answer as the answer is minimal and links are dead. I've posted a deeper explanation, link to source study including full reference name in case the link dies, explanation of it's performance at the time and why it is or will be obsolete to optimise Java in this way. I felt that a more complete answer was necessary and judging by my upvotes, a welcome one. – Ross Drew Dec 29 '21 at 09:36
  • @RossDrew - at some point in the past, someone has taken your code and committed it the Apache POI codebase - see https://bz.apache.org/bugzilla/show_bug.cgi?id=65796 - would it be possible to get your permission to reuse your code? Apologies for the inconvenience. – PJ Fanning Jan 10 '22 at 10:23
  • 3
    @RossDrew we removed the code from Apache POI - it turns out that in modern JVMs, Arrays.fill is optimised (http://psy-lob-saw.blogspot.com/2015/04/on-arraysfill-intrinsics-superword-and.html) - I used jmh to benchmark and found this to be case (Zulu 1.8.0_302 and 17.0.0 JVMs). – PJ Fanning Jan 10 '22 at 14:45
  • 1
    @PJFanning I knew that would at least eventually be the case when I wrote this. That's why I put that last part in, to reflect that massive benefit of Java; your code will just get faster. – Ross Drew Jan 10 '22 at 15:37
14

Use Arrays.fill

  char f = '+';
  char [] c = new char [50];
  Arrays.fill(c, f)
soulcheck
  • 36,297
  • 6
  • 91
  • 90
8

Java Programmer's FAQ Part B Sect 6 suggests:

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

This essentially makes log2(array.length) calls to System.arraycopy which hopefully utilizes an optimized memcpy implementation.

However, is this technique still required on modern Java JITs such as the Oracle/Android JIT?

Bill Tutt
  • 414
  • 4
  • 6
6

System.arraycopy is my answer. Please let me know is there any better ways. Thx

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-looping takes 133536 nano-seconds.
System.arraycopy looping takes 22070 nano-seconds.
One round Arrays.fill takes 9777 nano-seconds.
Two rounds Arrays.fill takes 93028 nano-seconds.

12:33:38
Arrays' 2-looping takes 133816 nano-seconds.
System.arraycopy looping takes 22070 nano-seconds.
One round Arrays.fill takes 17042 nano-seconds.
Two rounds Arrays.fill takes 95263 nano-seconds.

12:33:51
Arrays' 2-looping takes 199187 nano-seconds.
System.arraycopy looping takes 44140 nano-seconds.
One round Arrays.fill takes 19555 nano-seconds.
Two rounds Arrays.fill takes 449219 nano-seconds.

12:34:16
Arrays' 2-looping takes 199467 nano-seconds.
System.arraycopy looping takes 42464 nano-seconds.
One round Arrays.fill takes 17600 nano-seconds.
Two rounds Arrays.fill takes 170971 nano-seconds.

12:34:26
Arrays' 2-looping takes 198907 nano-seconds.
System.arraycopy looping takes 24584 nano-seconds.
One round Arrays.fill takes 10616 nano-seconds.
Two rounds Arrays.fill takes 94426 nano-seconds.

odys
  • 61
  • 1
  • 2
  • 7
    Why did you pick System.arraycopy(...) when your logs state that Arrays.fill(...) was faster every run? Generally, it was massively faster! – ingyhere Apr 29 '14 at 09:38
  • 1
    For people reading this answer in case you miss odys's comment at the start of the method. One round Arrays.fill actually is using Arrays.fill(Object[],Object) which wil fill the outer array with references to r1 (i.e. setting afterwards r2[i][j] = 42 will set r2[x][j] = 42 for all x), clearly not the intended behavior. – ggf31416 Oct 29 '18 at 06:10
6

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.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 1
    I found setall more performant when setting an array (size 500) to a default value than using fill by about 3x – Nrj Jun 15 '20 at 22:32
4

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);
  }
}
radfast
  • 538
  • 1
  • 7
  • 14
  • Another minor improvment could be removing the conditional: `for (int i = SMALL, mask; i < len; i += i) {mask = (len - i + i) >> 31; System.arraycopy(array, 0, array, i, ~mask & len - i | mask & i);}` I have not benchmarked this in practice so it might not be. – RipeGorilla Feb 09 '21 at 21:26
  • interesting! In principle, when compiled to native code I agree that a few arithmetic and bit operations could be faster, on a modern CPU, than a conditional branch. But there will be conditional branching inside `System.arraycopy` which we can do nothing about... – radfast Feb 11 '21 at 20:20
3

If you have another array of char, char[] b and you want to replace c with b, you can use c=b.clone();.

Dragos
  • 2,911
  • 12
  • 39
  • 55
  • Or, if your arrays are variable length, create a "super long" prototype and use System.arraycopy. Either will effectively do a `memcpy` below the covers. – Hot Licks Feb 03 '12 at 12:42
2

See Arrays.fill method:

char f = '+';
char [] c = new char [50];
Arrays.fill(c, f);
Mariusz Jamro
  • 30,615
  • 24
  • 120
  • 162
1

Arrays.fill might suit your needs

DaveH
  • 7,187
  • 5
  • 32
  • 53
1

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.

John B
  • 32,493
  • 6
  • 77
  • 98
  • The reference to array.length is cheap (not a call) and is easily optimized out of the loop. – Hot Licks Feb 03 '12 at 12:47
  • @HotLicks Are you sure that the compiler does this? Isn't it possible for the array reference to change (and therefore the size) within the loop? Therefore would it be safe for the compiler to optimize this out? I suppose if it were smart enough to ensure that the array reference is not modified inside the loop it could do so. Again, do you have some knowledge that this optimization is being done? – John B Feb 03 '12 at 12:50
  • @HotLicks Can I assume your statement holds try of arrays but not collections? – John B Feb 03 '12 at 12:50
  • `array` is not changed in the loop -- very easy for the compiler to determine. And the `arraylength` bytecode directly references the array size field in the array object, so it's virtually as cheap if left in the loop as if moved out. Even Sun's own code (in my post) doesn't worry about moving the reference out of the loop. The biggest performance boost is obtained in the more optimized version of Sun's code shown in the addendum to Paranoid's original -- there a special/secret flag on the method turns off bounds checking, and explicit bounds checking is done outside the loop. – Hot Licks Feb 03 '12 at 17:15
1
   /**
     * 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.)

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
0

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.

user2179737
  • 493
  • 3
  • 6
0

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);
DNA
  • 42,007
  • 12
  • 107
  • 146