Java 8 потоки: IntStream для строки

В API Java 8 streams вызов chars() для любого объекта String возвращает объект IntStream, содержащий все символы.

Каким будет правильный способ преобразования возвращаемого объекта IntStream обратно в String? Вызов toArray() дал бы мне int[], который не принимается никаким конструктором String.

Ответ 1

Вы можете использовать toArray(), затем конструктор String(int[], int, int). Это не совсем удовлетворительно, поскольку chars() указан для возврата кодовых единиц UTF-16, в основном:

Возвращает поток int zero, расширяющий значения char из этой последовательности. Любой char, который сопоставляется суррогатной кодовой точке, передается через неинтерпретируемый.

Использование codePoints() вместо этого будет более соответствовать этому конструктору, который ожидает кодовые точки, а не кодовые единицы UTF-16. В противном случае (с chars), если ваша исходная строка содержит суррогатные пары, вы можете обнаружить, что получите ошибку - я ее не пробовал, но это имело бы смысл.

Я не знаю простого способа сделать это, не обращаясь сначала к массиву.

Ответ 2

Я уверен, что должно быть много способов сделать это, но другим способом является использование StringWriter:

IntStream in = "It was the best of times".chars();
StringWriter sw = new StringWriter();
in.forEach(sw::write); 
System.out.println(sw.toString());

Все это также может быть выражено в коллекторе как:

IntStream in = "It was the best of times".chars();
String text = in.collect(
    StringWriter::new, 
    StringWriter::write, 
    (swl, swr) -> swl.write(swr.toString())).toString();
System.out.println(text);

Ответ 3

используя метод StringBuilder appendCodePoint, сделал бы трюк,

IntStream in = "Convert me to a String".codePoints();

String intStreamToString = in.collect(StringBuilder::new,
        StringBuilder::appendCodePoint, StringBuilder::append)
        .toString();

System.out.println(intStreamToString);

Ответ 4

Это еще одна идея:

@Test
public void testIntStreamSequential() {
    final String testString = "testmesoftly";
    IntStream is = testString.chars();
    String result = is.collect(
        StringBuilder::new,
        (sb, i) -> sb.append((char)i),
        StringBuilder::append
        ).toString();
    assertEquals(testString, result);
}

@Test
public void testIntStreamParallel() {
    final String testString = "testmesoftly";
    IntStream is = testString.chars();
    String result = is.parallel().collect(
        StringBuilder::new,
        (sb, i) -> sb.append((char)i),
        StringBuilder::append
        ).toString();
    assertEquals(testString, result);
}

Обратите внимание, что использование выделенного Collector, предложенного @Lii, не очень эффективно, из-за бокса, поэтому вы должны использовать эту конструкцию с тремя аргументами (спасибо @holger)