如何在 Java Stream API 中"::"方法引用运算符附近执行其他处理



使用Java Stream API,有没有办法进行额外的处理来调整传递给方法引用的任何值?

我举两个例子。

例 1.

在第一个示例中,我从Stream<Path>开始,我想返回一个Map<String, Path>,其中映射中的键使用另一个采用String文件名(不是Path)的函数处理文件名的版本。具体说来:

public Map<String, Path> createMap(Path sourceFolder, PathMatcher filter) {
return stream.filter(filter::matches)
.collect(Collectors.toMap(FilenameHelper::parseFilename, Function.identity()));

parseFilename(String filename)采用字符串文件名,但方法引用当然会获取路径。我想说这样的话,FilenameHelper::parseFilename(((Path)Function.identity()).toFile().getName())但这行不通(Eclipse 说:"赋值的左侧必须是一个变量")。我可以通过创建一种新方法来解决它,该方法需要Path并且只是return parseFilename(path.toFile().toName())但这并不酷。

例 2.

在第二个示例中,我有rows,一个表示数据表(行,然后是列)的List<List<String>>>。我有一个方法,应该为每 n 行返回一个由该表中特定列组成的List<String>。我想做这样的事情:

public List<String> getDataFromColumn(String columnName, int nth) {
/// Without a clause at ???, this returns a List<List<String>>
return IntStream.range(0, rows.size())     
.filter(n -> n % nth == 0) // Get every nth row
.mapToObj(rows::get)       
.???
.collect(Collectors.toList());
}

其中"???"应该是类似于map(ArrayList::get(headers.indexOf(columnName)))的东西(其中headers是包含列标题的List<String>),但如果我把它放进去,我会在这个子句的get部分得到一个AssignmentOperator语法错误。用forEach替换map在这里无济于事。换句话说,我不要rows.get(n),我要rows.get(n).get(headers.indexOf(columnName)

问题

在这两个示例中,我想对传递给使用方法引用运算符 (::指向的方法的值执行其他操作。是否有一种"Java Stream-ic"方法可以对传递给方法引用的东西进行额外的处理?

方法引用本质上是 lambda 的方便替代品,其中函数签名与方法签名完全匹配。在您的情况下,您可以只使用常规 lambda:

public Map<String, Path> createMap(Path sourceFolder, PathMatcher filter) {
return stream.filter(filter::matches)
.collect(Collectors.toMap(path -> FilenameHelper.parseFilename(path.toFile().getName()), Function.identity()));
}

public List<String> getDataFromColumn(String columnName, int nth) {
return IntStream.range(0, rows.size())
.filter(n -> n % nth == 0)
.mapToObj(rows::get)
.map(row -> row.get(headers.indexOf(columnName)))
.collect(Collectors.toList());
}

Function.compose怎么样?当然你不能使用FilenameHelper::parseFilename.compose,但你可以很容易地编写一个静态帮助程序方法来解决这个问题:

static <T, V, R> Function<T, R> compose(Function<T, V> f, Function<V, R> g) {
return g.compose(f);
}

现在我们可以编写方法引用:

return stream.filter(filter::matches)
.collect(Collectors.toMap(
compose(
compose(Path::getFileName, Path::toString), 
FilenameHelper::parseFilename),
Function.identity()));

这实际上不是很好读,而是编写完整lambda的替代方法。

否,目前不提供此功能。

通常的方法是不使用方法引用,而是使用 lambda 表达式以"通常"方式调用该方法:

stream.filter(filter::matches)
.collect(Collectors.toMap(p -> FilenameHelper.parseFilename(p.getFileName()), Function.identity()));

不,没有。没有语法可以做到这一点。 如果你想要这样的东西,那么lambda表达式就是你想要的。

方法引用或lambda,在引擎盖下,您仍然会得到一个实际实现谓词/函数的类,因此无关紧要。

这个论点并不,对我来说,在没有语法的情况下,这是你最好的选择。

在实际调用的下面有一个 MethodHandle(在 jdk-7 中引入),并且 MethodHandle 没有办法实现您想要的。我认为在方法指针C++中存在相同的限制。

最新更新