Как отфильтровать строки, которые не являются допустимыми значениями перечисления, в потоке Java 8? - PullRequest
2 голосов
/ 13 февраля 2020

У меня есть enum Я звоню Permission разрешений безопасности, которые важны для моего приложения. В базе данных пользователь может иметь дополнительные разрешения, которые не относятся к моему приложению. При чтении пользователя из базы данных я получаю List<String> и хочу создать List<Permission>, игнорируя те строки, которые не являются значениями перечисления.

public enum Permission { ADMIN, USER }

List<String> plaintextAuthorities = List.of("ADMIN","USER","COFFEEMAKER")

List<Permission> appPermissions = plaintextAuthorities.stream()
        .map(Permission::valueOf) // this will throw an IllegalArgumentException for 'COFFEEMAKER'
        .collect(Collectors.toList());

Проблема в том, что шаг map выдаст исключение, когда достигнет одной из строк, которая не соответствует значению перечисления. Как я могу filter вывести эти значения, чтобы они тихо игнорировались без исключения?

Ответы [ 3 ]

6 голосов
/ 13 февраля 2020

Вы можете сохранить имена в самом перечислении (как Map):

public enum Permission {
    ADMIN,
    USER;

     static final Map<String, Permission> MAP =
        EnumSet.allOf(Permission.class)
               .stream()
               .collect(Collectors.toUnmodifiableMap(
                   Enum::name,
                   Function.identity()
               ));        

}

А затем просто сделать:

List<Permission> permissions =
        plaintextAuthorities.stream()
                            .map(Permission.MAP::get)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList());
6 голосов
/ 13 февраля 2020

Вы можете сначала кодировать константы перечисления Permission в Set строки, а затем использовать это в предикате filter для фильтрации недопустимых значений. Затем используйте оператор .map для декодирования констант перечисления обратно из строкового представления только для допустимых значений. Вот как это выглядит.

Set<String> strPermissionSet = Arrays.stream(Permission.values())
    .map(Permission::name)
    .collect(Collectors.toSet());

Set<Permission> appPermissions = plaintextAuthorities.stream()
    .filter(strPermissionSet::contains)
    .map(Permission::valueOf)
    .collect(Collectors.toSet());

Тем не менее лучше всего объявить в своем типе enum метод stati c с именем isValid, чтобы при заданном строковом значении пользователь мог проверить, является ли он действительным константа перечисления или нет. Вот как это выглядит.

public enum Permission {
    ADMIN, USER;

    private static final Set<String> strPermissions = 
        Arrays.stream(values())
        .map(Permission::name)
        .collect(Collectors.toSet());

    public static boolean isValid(String val) {
        return strPermissions.contains(val);
    }
}

А теперь ваш клиентский код должен выглядеть так:

Set<Permission> appPermissions = plaintextAuthorities.stream()
    .filter(Permission::isValid)
    .map(Permission::valueOf)
    .collect(Collectors.toSet());
0 голосов
/ 05 марта 2020

И Равиндра, и Евгений отправили отличные ответы. Мой собственный ответ менее изящен и, вероятно, медленнее, но у него есть одно преимущество: я мог бы реализовать его без изменения класса enum. В вызывающем классе я добавил метод:

private boolean isAValidPermission(String s) {
    try {
        Permission.valueOf(s);
        return true;
    } catch(IllegalArgumentException e) {
        return false;
    }
}

И добавил один «фильтр» в выражение потока:

List<Permission> appPermissions = plaintextAuthorities.stream()
    .filter(this::isAValidPermission)
    .map(Permission::valueOf)
    .collect(Collectors.toList());
...