Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Repeating the elements of an array

Tags:

java

arrays

How can I repeat the elements of an array in Java?

For example, given the array {a,b,c,d,e,f} and a number n, I want to produce an n-element array that looks like {a,b,c,d,e,f,a,b,c,d,e,f,a,b,c,...}.

If I knew the length of the input and output arrays in advance, I could just write something like this:

int a=input[0], b=input[1], c=input[2], d=input[3], e=input[4], f=input[5];

int[] array = new int[n];
array[0]=a; array[1]=b; array[2]=c; array[3]=d; array[4]=e; array[5]=f;
array[6]=a; array[7]=b; array[8]=c; array[9]=d; array[10]=e; array[11]=f;
array[12]=a; array[13]=b; array[14]=c; // .. and so on

But how can I do this if I don't know the lengths yet? I assume I would have to use a loop of some kind, but I'm not sure how to write one. Or is there some built-in way to repeat an array in Java, like some other languages have?

like image 555
chandu061 Avatar asked Aug 31 '15 07:08

chandu061


1 Answers

This implementation is much cleaner and faster than the others shown here.

public static <T> T[] repeat(T[] arr, int newLength) {
    T[] dup = Arrays.copyOf(arr, newLength);
    for (int last = arr.length; last != 0 && last < newLength; last <<= 1) {
        System.arraycopy(dup, 0, dup, last, Math.min(last << 1, newLength) - last);
    }
    return dup;
}

Theory

System.arraycopy is a native call. Therefore it is very fast but it doesn't mean it is the fastest way.

Every other solution copys the array element by element. My solution copys larger blocks. Every iteration duplicates the existing elements in the array which means the loop will run at most log2(n) times.

Profiling reports

Here is my benchmark code to reproduce the results:

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;

@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@Measurement(iterations = 10, timeUnit = TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Threads(1)
@Warmup(iterations = 5, timeUnit = TimeUnit.NANOSECONDS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class MyBenchmark {

  private static final String[] TEST_ARRAY = { "a", "b", "c", "d", "e", "f" };
  private static final int NEW_LENGTH = 10_000;

  @Benchmark
  public String[] testMethod() {
    String[] dup = Arrays.copyOf(TEST_ARRAY, NEW_LENGTH);
    for (int last = TEST_ARRAY.length; last != 0 && last < NEW_LENGTH; last <<= 1) {
      System.arraycopy(dup, 0, dup, last, Math.min(last << 1, NEW_LENGTH) - last);
    }
    return dup;
  }

  @Benchmark
  public String[] testMethod1() {
    String[] arr = new String[NEW_LENGTH];
    for (int i = 0; i < NEW_LENGTH; i++) {
      arr[i] = TEST_ARRAY[i % TEST_ARRAY.length];
    }
    return arr;
  }

  @Benchmark
  public String[] testMethod2() {
    List<String> initialLetters = Arrays.asList(TEST_ARRAY);
    List<String> results = new ArrayList<>();
    int indexOfLetterToAdd = 0;
    for (int i = 0; i < 10000; i++) {
      results.add(initialLetters.get(indexOfLetterToAdd++));
      if (indexOfLetterToAdd == initialLetters.size()) {
        indexOfLetterToAdd = 0;
      }
    }
    return results.toArray(new String[results.size()]);
  }

  @Benchmark
  public String[] testMethod3() {
    String result[] = new String[NEW_LENGTH];
    for (int i = 0, j = 0; i < NEW_LENGTH && j < TEST_ARRAY.length; i++, j++) {
      result[i] = TEST_ARRAY[j];
      if (j == TEST_ARRAY.length - 1) {
        j = -1;
      }
    }
    return result;
  }

  @Benchmark
  public String[] testMethod4() {
    String[] result = Stream.iterate(TEST_ARRAY, x -> x).flatMap(x -> Stream.of(TEST_ARRAY)).limit(NEW_LENGTH)
        .toArray(String[]::new);
    return result;
  }
}

Results

Benchmark                Mode  Cnt      Score      Error  Units
MyBenchmark.testMethod   avgt   30   4154,553 ±   11,242  ns/op
MyBenchmark.testMethod1  avgt   30  19273,717 ±  235,547  ns/op
MyBenchmark.testMethod2  avgt   30  71079,139 ± 2686,136  ns/op
MyBenchmark.testMethod3  avgt   30  18307,368 ±  202,520  ns/op
MyBenchmark.testMethod4  avgt   30  68898,278 ± 2488,104  ns/op

EDIT

I rephrased this question and answered it with more precise benchmarks as it was suggested. Fastest way to create new array with length N and fill it by repeating a given array

like image 156
Flown Avatar answered Nov 08 '22 18:11

Flown