繁体   English   中英

如何使用 Lambda 和 Streams 反转 Java 8 中的单个字符串?

[英]How can I reverse a single String in Java 8 using Lambda and Streams?

我有一个字符串说"Aniruddh" ,我想在 Java 中使用 lambda 和流来反转它 8. 我该怎么做?

给定一个字符串

String str = "Aniruddh";

规范的解决方案是

String reversed = new StringBuilder(str).reverse().toString();

如果,也许出于教育目的,您想通过流式传输字符串的字符来解决此问题,您可以这样做

String reversed = str.chars()
    .mapToObj(c -> (char)c)
    .reduce("", (s,c) -> c+s, (s1,s2) -> s2+s1);

这不仅要复杂得多,而且还有很多性能缺陷。

以下解决方案消除了与拳击相关的开销

String reversed = str.chars()
    .collect(StringBuilder::new, (b,c) -> b.insert(0,(char)c), (b1,b2) -> b1.insert(0, b2))
    .toString();

但效率仍然较低,因为插入基于数组的缓冲区的开头意味着复制所有先前收集的数据。

所以底线是,对于实际应用程序,保持在开头显示的规范解决方案。

尝试使用 lambda 和流反转字符串

import java.util.stream.Stream;
import java.util.stream.Collectors;

    public class Test  {


        public static void main(String[] args) {

            System.out.println(reverse("Anirudh"));;
        }
        public static String reverse(String string) {
            return Stream.of(string)
                .map(word->new StringBuilder(word).reverse())
                .collect(Collectors.joining(" "));
        }
    }

如果你真的想为了学习而这样做,为什么不反转 char 数组呢?

public static String reverse(String test) {
    return IntStream.range(0, test.length())
            .map(i -> test.charAt(test.length() - i - 1))
            .collect(StringBuilder::new, (sb, c) -> sb.append((char) c), StringBuilder::append)
            .toString();
}

另一种反转字符串的方法。 您可以使用 IntStream 从char数组中提取正确的字符。

public static void main(String[] args) {
    char[] charArray = "Aniruddh".toCharArray();
    IntStream.range(0, charArray.length)
        .mapToObj(i -> charArray[(charArray.length - 1) - i])
        .forEach(System.out::print);
}

使用流实现您所要求的最简单方法可能是:

String result = Stream.of("Aniruddh").map(__ -> "hddurinA").findFirst().get();
Function<String, String> reverse = s -> new StringBuilder(s).reverse().toString();

这是另一种方式,看起来效率不高,但会解释原因:

String s = "blast";
IntStream.range(0, s.length()).           // create index [0 .. s.length - 1] 
   boxed().                               // the next step requires them boxed
   sorted(Collections.reverseOrder()).    // indices in reverse order
   map(i -> String.valueOf(s.charAt(i))). // grab each index's character 
   collect(Collectors.joining());         // join each single-character String into the final String

如果有一种方法可以附加所有字符而不将每个字符转换为String然后加入它们会更好。 这就是为什么我说它看起来效率不高。

您可以使用以下技术使用 stream 反转字符串。

String str = "Aniruddh";

Stream.iterate(str.length()-1, n-> n >= 0, n-> n-1)
        .map(input::charAt)
        .forEach(System.out::print);

另一种替代方法是将字符串拆分为字符串数组并在其上使用 reduce()。

Stream.of("Aniruddh".split("")).reduce("", (reversed, character) -> character + reversed);

给定一个长度为 S 的字符串,反转整个字符串而不反转其中的单个单词。 单词用点分隔。

String str="abcd.efg.qwerty";

String reversed = Arrays.asList(str.split("\\.")).stream().map(m -> new 
StringBuilder(m).reverse().toString()).collect(Collectors.joining("."));

System.out.println(reversed);
String str = "Noorus Khan";
       int len = str.length();
        IntStream.range(0, len)
                .map(i -> len - 1 - i)
                .mapToObj(j->str.charAt(j))
                .forEach(System.out::print);

输出 :: nahK surooN

以下是使用 Java 8 流 API 反转输入字符串str的另一种方法。

String abc = Arrays.asList(str).stream()
    .map(s -> new StringBuilder(s).reverse().toString())
    .collect(Collectors.toList()).get(0);

如果你想用Stream API实现自定义收集器,它会很方便(我将此任务专门视为练习)。

由于这个问题的贡献者都没有提到这种可能性,这里简要说明如何做到这一点。

创建自定义收集器

您可以通过使用 static 方法Collector.of()的版本之一内联或通过创建实现Collector接口的class来创建自定义收集器。

在此处输入图像描述

  • Supplier Supplier<A>旨在提供一个可变容器,用于存储 stream 的元素。它可以是CollectionStringBuilder或任何其他可变 object。
  • Accumulator BiConsumer<A,T> 定义如何将元素添加到供应商提供的容器中。
  • Combiner BinaryOperator<A> combiner()建立了在并行执行的情况下如何合并两个容器的规则。
  • Finisher Function<A,R>旨在通过转换可变容器来产生最终结果。
  • Characteristics允许提供额外的信息,例如Collector.Characteristics.UNORDERED表示收集器不保证在 stream 的源被排序时保留元素的初始顺序,这可以提供更好的并行性能。

最小参数集归结为supplieraccumulatorcombiner ,它们类似于collect()操作的 arguments ,但有一个区别 - of()期望的 combiner 参数(以及combiner()方法的返回类型)是BinaryOperator<A>

实现

第一个收集器背后的想法是使用ArrayDeque作为可变容器,并将代码点stream 中的每个元素添加到deque的前面。

并行创建的部分任务将导致必须以相反的顺序组合多个双端队列 因此,在组合器中,左双端队列包含更接近源字符串开头的值)被附加到右双端队列包含更接近源字符串末尾的值),而右双端队列作为结果。

Finisher 生成代码点数组int[]并返回基于它的字符串。

Collector<Integer, Deque<Integer>, String>

String source = "abcdefgh";

String reversed = source.codePoints()
    .boxed()
    .collect(Collector.of(
        ArrayDeque::new,
        Deque::addFirst,
        (Deque<Integer> left, Deque<Integer> right) -> { right.addAll(left); return right; },
        (Deque<Integer> deque) -> new String(
            deque.stream().mapToInt(Integer::intValue).toArray(),
            0,
            deque.size())
    ));

第二个收集器使用char[]作为其可变容器,该容器根据源IntStream的索引进行填充。

Combiner通过查看数组中每个position的最大值来合并两个arrays。

Finisher 使用构造函数new String(char[])生成结果字符串。

Collector<Integer, char[], String>

String reversed = IntStream.range(0, source.length())
    .boxed()
    .collect(Collector.of(
        () -> new char[source.length()],
        (char[] arr, Integer i) -> arr[(source.length() - 1) - i] = source.charAt(i),
        (char[] left, char[] right) -> {
            for (int i = 0; i < left.length; i++) left[i] = (char) Math.max(left[i], right[i]); return left;
        },
        String::new
    ));

Output对于字符串"abcdefgh"

hgfedcba

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM