Home > database >  Convert List<char[]> to char[] without using System.arraycopy?
Convert List<char[]> to char[] without using System.arraycopy?

Time:06-20

What's a simple way to convert/flatten a List<char[]> to char[] in Java?

I know I can do it by iterating the List and using System.arraycopy, but I'm wondering is there a simpler way to do it using Java 8 streams?

Maybe something like this, but without having to box the primitive char to Character:

List<char[]> listOfCharArrays = ...

Character[] charArray =
    Stream.of(listOfCharArrays ).flatMap(List::stream).toArray(Character[]::new);

CodePudding user response:

This is the most readable version I can come up with. You can append all the char arrays to a String, via a StringBuilder, then convert that to a char[].

char[] chars = listOfCharArrays.stream()
    .collect(Collector.of(StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString))
    .toCharArray();

Probably much slower than the iterative version, since arrayCopy can copy blocks of memory.

You could consider precomputing the total number of chars to avoid StringBuilder array reallocations, but this optimization and any others are going to eat into the readability gains you're getting from using streams.

int totalSize = listOfCharArrays.stream().mapToInt(arr -> arr.length).sum();
char[] chars = listOfCharArrays.stream()
    .collect(Collector.of(() -> new StringBuilder(totalSize), //... the same

CodePudding user response:

Maybe not the best solution, but you can use:

char[] chars = tableRowContainingOnlyRequestedColumns.stream()
        .map(String::valueOf)
        .collect(Collectors.joining())
        .toCharArray();

CodePudding user response:

It can be done via String.

char[] flatten(List<char[]> list) {
    return list.stream()
        .map(String::new)
        .collect(Collectors.joining())
        .toCharArray();
}

This requires "completed" array without any incomplete surrogate pair of chars at begin or end.

So compare this with:

char[] flatten(List<char[]> list) {
    int totalLength = list.stream().mapToInt(char[]::length).sum();
    char[] totalArray = new char[totalLength];
    int i = 0;
    for (char[] array : list) {
        System.arraycopy(array, 0, totalArray, i, array.length);
        i  = array.length; 
    }
    return totalArray;
}

Not so big a difference, and more solid code.

Or bring the entire software on the immutable String instead of char[].

CodePudding user response:

I can think of only one thing, and that is to use CharBuffer. For efficiency reasons I would always first calculate the right size, and then perform the copy. Any solution that performs multiple copies and/or performs string handling will be inefficient.

Of course, I cannot guarantee that it won't use System.arrayCopy somewhere inside of the CharBuffer#put method. I would actionally strongly expect that it will use that or similar code. That however goes for most solutions provided here, in all probability.

CharBuffer fullBuffer = CharBuffer.allocate(listOfCharArrays.stream().mapToInt(array -> array.length).sum());
listOfCharArrays.stream().forEach(fullBuffer::put);
char[] asCharArray = fullBuffer.array();

So although I don't know your reason to avoid System.arrayCopy, but at least the above code is relatively efficient.

You can of course avoid the first size calculation by using a large enough buffer if you can estimate a maximum size, but it would require another copy of the data; fullBuffer#array() simply returns the backing array, which means that the data is copied only once.

  •  Tags:  
  • java
  • Related