【问题标题】:Is there a Java Stream method equivalent to Scala's collections "collect"?是否有等效于 Scala 的集合“收集”的 Java Stream 方法?
【发布时间】:2014-12-19 03:15:50
【问题描述】:

Scala 的集合提供了一个名为collect 的方法,它将filtermap 合并为一个方法。在过滤 Object 集合以生成该集合的仅包含特定类型的子集时,它特别有用。

Java 8 的 Stream 有这样的东西吗?

【问题讨论】:

  • 我想你已经知道你可以通过链接mapfiltercollect 调用来做正确的事情。那么您请求在一个方法调用中执行此操作的目的是什么?因为“感觉”就像更有效率?
  • @Holger 它将前置条件与映射函数联系起来,这有助于明确一个 与另一个的前置条件,并且无法打破通过更改方法调用的顺序来编写代码。
  • 其实恰恰相反。没有人可以从包含映射函数和首先应用的谓词的单个方法的签名中分辨出来。相比之下,有了Stream API,大家就明白map(…).filter(…).collect(…).filter(…).map(…).collect(…) 的区别了。甚至可以推断出其他组合的含义,如map(…).filter(…).map(…).collect(…)...
  • @Holger 实际上,给定参数 T -> Boolean 和 T -> R,对于输入 Stream[T] 和输出 Stream[R],只有两个没有副作用的逻辑实现,并且在这两个其中 T -> 布尔值在前。
  • mapfilter 的组合也可以这样说;顺序可以从泛型类型签名中推导出来,但不同的是,对于方法链,即使不知道签名,您也可以直观地识别顺序。

标签: java java-8 java-stream


【解决方案1】:

鉴于 Java 的类型系统,在一个步骤中组合 filtermap 在 Java 中并没有多大用处。例如。没有这样的PartialFunction 类型。

最接近的是flatMap:

List<Object> list=Arrays.asList(0, "foo", 0L, 42.0, true, "false");
List<String> sList = list.stream()
  .flatMap(o-> o instanceof String? Stream.of((String)o): Stream.empty())
  .collect(Collectors.toList());
System.out.println(sList);

但表现力

.flatMap(o-> o instanceof String? Stream.of((String)o): Stream.empty())

相比没有那么大
.filter(o -> o instanceof String).map(o -> (String)o)

当然,您可以创建一个辅助方法,将这些步骤封装在一个单一的、不可分解的操作中:

static <T> Stream<T> onlyType(Stream<?> s, Class<T> type) {
    return s.filter(type::isInstance).map(type::cast);
}

那么你可以像这样使用它

List<String> sList=onlyType(list.stream(), String.class).collect(Collectors.toList());

【讨论】:

    【解决方案2】:

    我正在尝试在某处阅读时提供您问题的答案 -

    <R,A> R collect(Collector<? super T,A,R> collector)
    

    R - 结果的类型 A - Collector 的中间累积类型 收集器 - 描述减少的收集器 返回 - 归约的结果

    使用收集器对此流的元素执行可变归约操作。 Collector 封装了用作 collect(Supplier, BiConsumer, BiConsumer) 的参数的函数,允许重用收集策略和组合收集操作,例如多级分组或分区。 如果流是并行的,并且 Collector 是并发的,并且流是无序的或者收集器是无序的,则将执行并发归约(有关并发归约的详细信息,请参见 Collector。)

    这是一个终端操作。

    当并行执行时,可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离。因此,即使与非线程安全的数据结构(如 ArrayList)并行执行,也不需要额外的同步来实现并行归约。

    以下会将字符串累积到一个ArrayList中:

    List<String> asList = stringStream.collect(Collectors.toList());
    

    以下将按州和城市对 Person 对象进行分类,将两个 Collector 级联在一起:

     Map<String, Map<String, List<Person>>> peopleByStateAndCity
         = personStream.collect(Collectors.groupingBy(Person::getState,
                                                      Collectors.groupingBy(Person::getCity)));
    

    【讨论】:

    • 我不是在谈论 Java 8 的 Streamcollect 方法。我说的是 Scala 的 collect 方法并要求 Java 8 中的等效方法。
    【解决方案3】:

    据我了解 Scala 文档,collect 根据函数是否存在进行过滤,并将函数应用于每个符合条件的对象。 Java 类有一个固定的定义,所以这样的测试没有多大意义。最接近的模拟是测试接口并在多个步骤中调用其方法(函数):

    stream.filter(e -> e instanceof SomeInterface).map(SomeInterface.class::cast).map(AnotherClass::convertSomeInterfaceToSomethingElse)
    

    【讨论】:

      【解决方案4】:

      据我所知,从 Stream 接口和一些支持库中,没有一个 单一 操作可以同时完成这两个操作并产生输出 Stream(基于在我对 Scala collect 方法的简短阅读中)。

      您需要将这两个函数应用于Stream 以获得所需的输出Stream,然后可以收集该输出或您想要应用于它的任何其他函数。

      collection.stream()
        .filter(predicateFunction)
        .map(mappingFunction)
        .collect(Collectors.toList()) 
      

      【讨论】:

      • 单个操作,但该操作是使用链式方法调用准备的,这只是提高了 API 的灵活性。
      【解决方案5】:

      这样试试

      tList.stream().map[E](func).collect(Collectors.toList[E])
      

      tList是你源列表的类型,E是你想要返回的类型。如果源类型与返回类型相同,则mapE中不需要[E],看起来是scala中的map函数返回类型是对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-20
        • 2010-10-19
        • 1970-01-01
        • 2012-11-06
        • 1970-01-01
        • 2016-11-10
        • 1970-01-01
        相关资源
        最近更新 更多