【问题标题】:How to shuffle a stream using the Stream API?如何使用 Stream API 随机播放流?
【发布时间】:2014-07-26 04:41:57
【问题描述】:

我决定采用函数式方法来生成字符串或随机字符,到目前为止我想出了这个,它应该比装箱然后使用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 中的函数式方法。

【问题讨论】:

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


    【解决方案1】:

    你想得太歪了

    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();
    

    (注意:我用rangeClosed 替换了range,我怀疑这符合您的初衷,但它并没有像Random.ints(…, 'a', 'z') 那样做)。

    【讨论】:

    • 不幸的是,它确实摆脱了明确的 'a' - 'z''A' - 'Z' 范围。
    • 这不是洗牌。这允许重复。改组意味着保留原始元素,而不是它们的顺序。
    • @Mark Jeronimus:这不是改组,但正如答案中所解释的,改组不是解决问题实际任务的正确工具,即使用生成随机String Stream API。随机的String 可能在改组之前有重复项,因此替代解决方案创建的String 也可能重复。
    【解决方案2】:

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

    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();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-01-23
      • 1970-01-01
      • 2023-03-23
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多