Java 8 流中间映射/收集到具有 2 个值的流

  • 本文关键字:中间 映射 Java java java-stream
  • 更新时间 :
  • 英文 :


想象一下,我有以下工作lambda表达式:

        Map<Field, String> fields = Arrays.stream(resultClass.getDeclaredFields())
            .filter(f -> f.isAnnotationPresent(Column.class))
            .collect(toMap(f -> {
                f.setAccessible(true);
                return f;
            }, f -> f.getAnnotation(Column.class).name()));

我想在过滤器语句之前创建一个包含 2 个值的流。所以我想做一个映射,但仍然保留原始值。我想实现这样的事情:

        this.fields = Arrays.stream(resultClass.getDeclaredFields())
            //map to <Field, Annotation> stream
            .filter((f, a) -> a != null)
            .collect(toMap(f -> {
                f.setAccessible(true);
                return f;
            }, f -> a.name()));

Java 8 流可以做到这一点吗?我已经看过 collect(groupingBy()),但仍然没有成功。

你需要一个包含两个值的Pair之类的东西。 您可以编写自己的代码,但这里有一些重新利用AbstractMap.SimpleEntry的代码:

     Map<Field, String> fields = Arrays.stream(resultClass.getDeclaredFields())
            .map(f -> new AbstractMap.SimpleEntry<>(f, f.getAnnotation(Column.class)))
            .filter(entry -> entry.getValue() != null)
            .peek(entry -> entry.getKey().setAccessible(true))
            .collect(toMap(Map.Entry::getKey, entry -> entry.getValue().name()));

您可以在collect操作期间一次性完成整个操作,而无需配对类型:

Map<Field, String> fields = Arrays.stream(resultClass.getDeclaredFields())
    .collect(HashMap::new, (m,f) -> {
        Column c=f.getAnnotation(Column.class);
        if(c!=null) {
            f.setAccessible(true);
            m.put(f, c.name());
        }
    }, Map::putAll);

不过,对我来说,将两个不合并在一起的操作分开看起来更干净:

Map<Field, String> fields = Arrays.stream(resultClass.getDeclaredFields())
    .collect(HashMap::new, (m,f) -> {
        Column c=f.getAnnotation(Column.class);
        if(c!=null) m.put(f,c.name());
    }, Map::putAll);
AccessibleObject.setAccessible(
    fields.keySet().stream().toArray(AccessibleObject[]::new), true);

此解决方案确实对具有注释的字段进行两次迭代,但由于它只执行一次安全检查,而不是每个字段执行一次检查,因此它的性能仍可能优于所有其他解决方案。

通常,除非确实存在性能问题,否则您不应该尝试优化,如果您这样做,您应该衡量,而不是猜测操作成本。结果可能令人惊讶,对数据集进行多次迭代并不一定是坏事。

@Peter Lawrey:我用中间地图尝试了你的建议。它现在可以工作,但它不是很漂亮。

this.fields = Arrays.stream(resultClass.getDeclaredFields())
            .collect(HashMap<Field, Column>::new, (map, f) -> map.put(f, f.getAnnotation(Column.class)), HashMap::putAll)
            .entrySet().stream()
            .filter(entry -> entry.getValue() != null)
            .peek(entry -> entry.getKey().setAccessible(true))
            .collect(toMap(Map.Entry::getKey, entry -> entry.getValue().name()));

最新更新