有没有与Scala集合中的“collect”相等的Java Stream方法?

7

Scala的集合提供了一个称为collect的方法,它将filtermap合并到一个单独的方法中。在过滤Object集合以产生只包含特定类型的该集合子集时,它尤其有用。

Java 8的Stream中是否有类似的功能呢?


1
我想你已经知道可以通过链接mapfiltercollect调用来做正确的事情。那么你请求在一个方法调用中完成这个过程的目的是什么呢?因为这样做“感觉”更有效率吗? - Holger
@Holger 它将前置条件与映射函数联系起来,这有助于澄清一个是另一个的前置条件,并且使改变方法调用顺序无法破坏代码。 - Daniel C. Sobral
3
实际上相反。从包含映射函数和谓词的单个方法签名中,没有人可以确定先应用哪个函数。相比之下,使用Stream API,每个人都理解 map(…).filter(…).collect(…).filter(…).map(…).collect(…) 之间的区别。甚至可以推断出其他组合的含义,如 map(…).filter(…).map(…).collect(…) - Holger
@Holger 实际上,对于输入流 Stream[T] 和输出流 Stream[R],给定参数 T -> Boolean 和 T -> R,只有两种没有副作用的逻辑实现方式,而且在这两种实现中,T -> Boolean 都是首要的。 - Daniel C. Sobral
2
你可以用同样的方式来组合 mapfilter;顺序可以从泛型类型签名中推断出来,但是不同之处在于对于方法链,即使不知道签名,你也可以直观地识别顺序。 - Holger
5个回答

9

在Java中,结合filtermap的操作并不是很实用,因为其类型系统的限制。例如,没有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());

0

试试这样

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

tList是您的源列表类型,E是您想要返回的类型。如果源类型与返回类型相同,则在mapE中不需要[E],就像Scala中的map函数返回类型为对象一样。


0

我正在尝试回答你的问题,因为我在某个地方读到了这个答案 -

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

R - 结果的类型 A - Collector 的中间累加器类型 collector - 描述 reduction 的 Collector 返回值 - reduction 的结果

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

这是一个终端操作。

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

以下代码将字符串累加到 ArrayList 中:

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

以下将通过州和城市对Person对象进行分类,级联两个Collectors:
 Map<String, Map<String, List<Person>>> peopleByStateAndCity
     = personStream.collect(Collectors.groupingBy(Person::getState,
                                                  Collectors.groupingBy(Person::getCity)));

我不是在谈论Java 8的Streamcollect方法。我在谈论Scala的collect方法,并询问Java 8中是否有相应的方法。 - Daniel C. Sobral

0
据我从Stream接口和一些支持库中了解,根据我对Scala collect方法的简短阅读,似乎没有一个单一的操作可以同时执行这两个操作并生成一个输出流。您需要将这两个函数都应用于一个流以获得所需的输出流,然后可以收集或应用任何其他想要应用的函数。
collection.stream()
  .filter(predicateFunction)
  .map(mappingFunction)
  .collect(Collectors.toList()) 

1
这确实是一个单一操作,但该操作是使用链接方法调用准备的,这只是提高了API的灵活性。 - Holger

0
据我理解Scala文档,collect是基于函数是否存在进行过滤,并将该函数应用于每个符合条件的对象。Java类具有固定的定义,因此这样的测试没有太多意义。最接近的类比是测试接口并在多个步骤中调用其方法(即函数):
stream.filter(e -> e instanceof SomeInterface).map(SomeInterface.class::cast).map(AnotherClass::convertSomeInterfaceToSomethingElse)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接