Java 8流和地图值得吗?

Cor*_*han 24 java functional-programming java-8 java-stream

感觉就像java 8流和映射函数那么冗长,它们并不是真正的改进.例如,我编写了一些使用集合生成另一个修改过的集合的代码:

private List<DartField> getDartFields(Class<?> model) {
    List<DartField> fields = new ArrayList<>();
    for (Field field : model.getDeclaredFields()) {
        if (!Modifier.isStatic(field.getModifiers())) {
            fields.add(DartField.getDartField(field));
        }
    }
    return fields;
}
Run Code Online (Sandbox Code Playgroud)

这似乎是java 8流及其功能的理想用例,所以我重写了它:

private List<DartField> getDartFields(Class<?> model) {
    return Arrays.asList(model.getDeclaredFields())
            .stream()
            .filter(field -> !Modifier.isStatic(field.getModifiers()))
            .map(field -> DartField.getDartField(field))
            .collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)

但我不确定我更喜欢那个.这是236个字符,而普通风格的java则是239个字符.它似乎没有或多或少可读.这是很好,你没有申报ArrayList,但需要打电话.collect(Collectors.toList())Arrays.asList(取决于数据类型)是没有任何好转.

使用.stream()这样的东西是否有一些实际的改进,我只是没有得到,或者这只是一个有趣的方式抛出任何同事不知道函数式编程的循环?

我想如果我动态地传递过滤器或映射lambdas它会很有用,但如果你不需要这样做......

Hol*_*ger 34

问题是您没有始终如一地使用StreamAPI .您正在将用例限制为可以最好地描述为"实际上不使用API"的内容,因为您坚持要返回a .这特别荒谬,因为它是一种方法,所以你完全能够适应呼叫者.StreamCollectionprivate

考虑将方法更改为

private Stream<DartField> getDartFields(Class<?> model) {
    return Stream.of(model.getDeclaredFields())
            .filter(field -> !Modifier.isStatic(field.getModifiers()))
            .map(field -> DartField.getDartField(field));
}
Run Code Online (Sandbox Code Playgroud)

并查看调用者实际想要做什么.通常它们Collection本身不需要作为目的,但是想要执行动作或甚至更多可以链接的操作,例如打印它们:

getDartFields(Foo.class).forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

最有趣的特性是流的惰性,这意味着getDartFields返回时,尚未执行任何操作,如果使用类似的操作findFirst,则无需处理所有元素.如果返回Collection包含所有元素的内容,则会丢失此功能.

这也适用于多步处理,其中处理普通列表意味着对于每个步骤,必须创建新列表并用结果填充.

  • 好(很棒)的答案。因为除非使用新的惰性范式,否则它很冗长。我不得不想知道为什么 Java 8 工作人员没有在数组和列表等上创建一些内在函数?所有数组都可以将 'foreach' 、 'filter' 、 'map' 作为第一类方法。Java要实现简洁还有很长的路要走!在此之前,Stream.of( ... ) 将成为我们的新朋友。 (3认同)
  • 荒谬有点苛刻.我发现传递溪流和流出的问题,主要是因为人们不止一次遍历它,所以我们倾向于在离开方法范围之前具体化. (2认同)

Pet*_*rey 8

你可以用不同的方式写出来(不一定更好)

private List<DartField> getDartFields(Class<?> model) {
    return Stream.of(model.getDeclaredFields())
            .filter(field -> !Modifier.isStatic(field.getModifiers()))
            .map(DartField::getDartField)
            .collect(Collectors.toList());
}
Run Code Online (Sandbox Code Playgroud)

使用静态导入看起来像

private static List<DartField> getDartFields(Class<?> model) {
    return of(model.getDeclaredFields())
            .filter(field -> !isStatic(field.getModifiers()))
            .map(DartField::getDartField)
            .collect(toList());
}
Run Code Online (Sandbox Code Playgroud)

它似乎没有或多或少可读.

这通常是恕我直言.但是,我会说,在> 10%的情况下,它明显更好.与任何新功能一样,您可能会过度使用它,直到您熟悉它并发现您使用它感觉舒适.

使用像我这样的.stream()是否有一些实际的改进,或者这只是一种有趣的方式来抛弃任何不知道函数式编程的循环的同事?

我怀疑两者.如果您不了解函数式编程,则它往往是只读代码.即你仍然可以理解它的作用,问题是如果你必须维护它.

恕我直言,值得鼓励开发人员学习函数式编程,因为它有一些关于如何构造代码的非常有用的想法,即使你没有使用FP语法,你也会从中受益.

如果Streams API在构造中很有用,那么您以前就不会有任何实现.

例如,假设您要按名称索引字段.

private static Map<String, DartField> getDartFields(Class<?> model) {
    return of(model.getDeclaredFields())
            .filter(field -> !isStatic(field.getModifiers()))
            .map(DartField::getDartField)
            .collect(groupingBy(f -> f.getName()));
}
Run Code Online (Sandbox Code Playgroud)

在过去,您可能使用了List而不是Map,但是通过使Map的组装更容易,您可能会使用您应该更频繁使用的数据结构.

现在让我们看看如果我们使用更多线程会更快.

private static Map<String, DartField> getDartFields(Class<?> model) {
    return of(model.getDeclaredFields()).parallel()
            .filter(field -> !isStatic(field.getModifiers()))
            .map(DartField::getDartField)
            .collect(groupingByConcurrent(f -> f.getName()));
}
Run Code Online (Sandbox Code Playgroud)

看看它有多难,当你发现它可能弊大于利时,将它改回来也很容易.

  • 太糟糕了,我不能接受关于这个的多个答案。很多人提出了我没有想到的好点。 (2认同)

小智 5

Java 8 流特别冗长,主要是因为转换为流然后又转换回另一种结构。在FunctionalJava中,等价的是:

private List<DartField> getDartFields(Class<?> model) {
    return List.list(model.getDeclaredFields())
        .filter(field -> !Modifier.isStatic(field.getModifiers()))
        .map(field -> DartField.getDartField(field))
        .toJavaList();
}
Run Code Online (Sandbox Code Playgroud)

我警告不要仅仅将字符数作为复杂性的衡量标准。这几乎不重要。

函数式编程允许您使用简单的替换模型来推理代码,而不必跟踪整个程序。这使得你的程序更加可预测和更容易,因为你头脑中一次需要的信息更少。

我还警告不要返回流。流不是任意可组合的,流是可变数据,调用者无法知道是否已在流上调用了终端操作。这意味着我们需要知道程序的状态才能推断正在发生的事情。引入流是为了帮助消除可变状态,但使用可变状态来实现 - 远非理想。

如果你想要一个不可变的流,我推荐Functional Java的流,https://functionjava.ci.cloudbees.com/job/master/javadoc/fj/data/Stream.html

  • 我认为消耗的流不会带来任何危险——这很容易检测和调试,而且这种错误在实践中不会真正发生。另外,我实际上“尊重”Streams API 处理可变性的方式:它提供了可变性的性能优势,但将实际的突变置于严格控制之下。 (3认同)