使用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++中存在相同的限制。