将 "stream in stream" 中的元素映射到 Set

Map elements from "stream in stream" to Set

我是 Java 流的新手。

我有一个数组 n 类。

类 有几个带有特定注释的字段 (SomeAnnotationClass.class)

我正在尝试获取一组使用此特定注释进行注释的所有字段注释值。如果字段没有注释我想要字段的名称。

所以我尝试了这样的事情:

     Stream.of(clazzes).map( c ->
        Stream.of((c.getDeclaredFields()))
            .map(
                field ->
                    Optional.ofNullable(
                        field.getDeclaredAnnotation(SomeAnnotationClass.class).value())
                        .orElse(field.getName())).collect(Collectors.toSet())).collect(Collectors.toSet());

2 个问题:

  1. 由于收集了 2 次,我得到的是 Set 而不是 Set。
  2. 如果注释不存在但调用了 SomeAnnotationClass.class.value(),我会得到一个空指针

我可以用流优雅地实现这个吗?

如@Andy Turner 所述,您可以使用 flatMap 将多个流映射到单个流并避免 NPE 在访问 value()[=14= 之前检查注释]

Set<String> value = clazzes.stream().map(c -> Stream.of((c.getDeclaredFields()))
               .map(field -> Optional.ofNullable(
                           field.getDeclaredAnnotation(SomeAnnotationClass.class)).map(SomeAnnotationClass::value).orElseGet(field::getName)).collect(Collectors.toSet()))
               .flatMap(Collection::stream).collect(Collectors.toSet());
package io.falcon.instagram.indexer.util;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.Enumerated;
import javax.persistence.Id;

public class Example {

    public static void main(String[] args) {
        List<Class<?>> classes = List.of(Test.class);
        
        Class<Enumerated> someAnnotationClass = Enumerated.class;
        
        Set<String> fieldNames = 
                classes.stream()
                .flatMap(c -> Arrays.stream(c.getDeclaredFields().clone())) // because getDeclaredFields returns array type
                .map((Field field) -> Optional.ofNullable(field.getDeclaredAnnotation(someAnnotationClass)).map(a -> field.getName()))
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toSet());
    
        System.out.println(fieldNames);
    }



    public static class Test {
        @Id
        private final String id;
        @Id
        @Enumerated
        private final String field;
        @Enumerated
        private final String another;
        @Enumerated
        private final String theGame;

        public Test(String id, String field, String another, String theGame) {
            this.id = id;
            this.field = field;
            this.another = another;
            this.theGame = theGame;
        }
    }
}

一组要展平:

// in Main.java
public static Set<String> getValuesOrNames(Class ... clazzes) {
    return Arrays.stream(clazzes)  // convert array to Stream<Class>
                 .flatMap(c -> Arrays.stream(c.getDeclaredFields())) // convert array of fields Stream<Field>
                 .map(field -> Optional.ofNullable(field.getAnnotation(SomeAnnotationClass.class))
                        .map(SomeAnnotationClass::value) // assuming SomeAnnotationClass has value method
                        .orElse(field.getName())
                 )
                 .collect(Collectors.toSet());
}

测试

// annotation class
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SomeAnnotationClass {
    String value() default "";
}
import java.util.*;
import java.util.stream.Collectors;
import lombok.Data;

public class Main {

    public static void main(String[] args) {
        System.out.println(getValuesOrNames(Something.class, Main.class));
    }

    @Data
    public static class Something {
        @SomeAnnotationClass(value = "String foo")
        private String foo;

        @SomeAnnotationClass
        private String emptyFoo;

        private String bar;

        @SomeAnnotationClass(value = "int id")
        private int id;
    }
}

输出

[, String foo, bar, int id]