如何使用Stream API混洗流?

时间:2014-06-05 09:04:18

标签: java functional-programming java-8 java-stream

我决定采用功能方法生成一个字符串或随机字符,到目前为止我想出了它,它应该比拳击更好,然后使用StringJoiner作为收集器:

Random random = new Random();
String randomString = IntStream.concat(
        random.ints(8, 'a', 'z'),
        random.ints(8, 'A', 'Z')
)
        .collect(
                StringBuilder::new,
                (sb, i) -> sb.append((char)i),
                (sb1, sb2) -> sb1.append(sb2)
        ).toString();

我想生成一个包含16个字符的流,范围从a-z或A-Z,我遇到的问题是我不知道如何对两个流进行混洗。

我知道我在这里使用IntStream.concat,这将简单地连接两个流,我正在寻找以下任何一种:

  • IntStream.concat这样的静态运算符,用于在合并流时进行混洗。
  • sorted()这样的流媒体运营商。

在我看来,这两种方式都是可行的,但我特别感兴趣的是如何使运营商一样sorted()。这里的关键点是它是一个有状态的运算符,因为它需要在它可以运行之前看到整个流,有没有办法在流序列中注入有状态运算符?

到目前为止,除了所需的工作之外,这些操作似乎不适合Java 8中的功能方法。

2 个答案:

答案 0 :(得分:10)

你觉得太扭曲了

Random random = new Random();
String randomString=random.ints(16, 0, 26*2).map(i->(i>=26? 'a'-26: 'A')+i)
  .collect(StringBuilder::new,
           StringBuilder::appendCodePoint, StringBuilder::append)
  .toString();

由于你已经有了一个随机值的来源,所以没有必要调用一个shuffle函数(这对 streams 来说效果不好)。

请注意,您还可以在String中明确定义允许的字符,并使用以下方法选择它们: random.ints(16, 0, allowed.length()).map(allowed::charAt)

类似的模式适用于从随机访问List中选择。


更新:如果您希望代码清楚地显示允许字符的两个范围性质,您可以将Stream.concat方法与上述char选择解决方案结合使用:

StringBuilder allowed=
  IntStream.concat(IntStream.rangeClosed('a', 'z'), IntStream.rangeClosed('A', 'Z'))
    .collect(StringBuilder::new,
             StringBuilder::appendCodePoint, StringBuilder::append);
String randomString=random.ints(16, 0, allowed.length()).map(allowed::charAt)
  .collect(StringBuilder::new,
           StringBuilder::appendCodePoint, StringBuilder::append)
  .toString();

(注意:我将range替换为rangeClosed,我怀疑它与您的原始意图相符,但不会执行Random.ints(…, 'a', 'z')会做的事情。

答案 1 :(得分:1)

这可能不像你希望的那样优雅,但它有效:

final Random random = new Random();
String randomString = IntStream.concat(random.ints(8, 'a', 'z'+1), random.ints(8, 'A', 'Z'+1))
    .collect(StringBuilder::new, (sb, i) -> {
      int l = sb.length();
      if (l == 0) {
        sb.append((char) i);
      } else {
        int j = random.nextInt(l);
        char c = sb.charAt(j);
        sb.setCharAt(j, (char) i);
        sb.append(c);
      }
    }, (sb1, sb2) -> sb1.append(sb2)).toString();
System.out.println(randomString);

或者你可以这样做:

final String randomString = random.ints(100, 'A', 'z' + 1)
        .filter(i -> i <= 'Z' || i >= 'a').limit(16)
        .collect(StringBuilder::new, (sb, i) -> sb.append((char) i), 
                StringBuilder::append).toString();