Java枚举上的逻辑操作

时间:2015-02-01 20:26:28

标签: java enums logical-operators

我有一个项目,在不同的场景中,我必须处理大型数据集的不同子集。我编写代码的方式有一个Collector接口和一个类DataCollector implements Collector。类DataCollector使用子集创建条件进行实例化,这些条件是枚举。

让我们说数据集是一组100万个英文单词,我想研究由奇数个字母组成的单词子集。然后,我执行以下操作:

DataCollector dataCollector = new DataCollector(CollectionType.WORDS_OF_ODD_LENGTH);
Set<String> fourLetteredWords = dataCollector.collect();

其中CollectionType是枚举类

enum CollectionType {
    WORDS_OF_ODD_LENGTH,
    WORDS_OF_EVEN_LENGTH,
    STARTING_WITH_VOWEL,
    STARTING_WITH_CONSONANT,
    ....
}

数据收集器调用java.util.Predicate,具体取决于实例化它的枚举。

到目前为止,这种方法已经足够强大和灵活,但现在我面临着越来越复杂的场景(例如,从元音开始收集均匀长度的单词)。我想避免为每个这样的场景添加新的CollectionType。我注意到的是,许多复杂场景只是对较简单场景的逻辑运算(例如condition_1 && (condition_2 || condition_3))。

最终用户是指定这些条件的人,而我唯一的控制是我可以指定这些条件的集合。同样,最终用户只能从CollectionType中进行选择。现在,我试图从仅选择一个条件的能力推广到能够选择一个或多个条件。为此,我需要像

这样的东西
DataCollector dataCollector = new DataCollector(WORDS_OF_ODD_LENGTH &&
                                                STARTING_WITH_VOWEL);

我是否有办法对我的枚举进行建模以执行此类操作?我对其他想法持开放态度(如果我只是废弃这种基于枚举的方法来做其他事情,等等。)

1 个答案:

答案 0 :(得分:13)

我建议您使用具有Predicate和支持谓词的操作的Java 8。

enum CollectionType implements Predicate<String> {
    WORDS_OF_ODD_LENGTH(s -> s.length() % 2 != 0),
    WORDS_OF_EVEN_LENGTH(WORDS_OF_ODD_LENGTH.negate()),
    STARTING_WITH_VOWEL(s -> isVowel(s.charAt(0))),
    STARTING_WITH_CONSONANT(STARTING_WITH_VOWEL.negate()),
    COMPLEX_CHECK(CollectionType::complexCheck);

    private final Predicate<String> predicate;

    CollectionType(Predicate<String> predicate) {
        this.predicate = predicate;
    }

    static boolean isVowel(char c) {
        return "AEIOUaeiou".indexOf(c) >= 0;
    }

    public boolean test(String s) {
        return predicate.test(s);
    }

    public static boolean complexCheck(String s) {
        // many lines of code, calling many methods
    }
}

你可以写一个类似

的谓词
Predicate<String> p = WORDS_OF_ODD_LENGTH.and(STARTING_WITH_CONSONANT);

甚至是以元音开头的五个字母单词

Predicate<String> p = STARTING_WITH_VOWEL.and(s -> s.length() == 5);

假设您想在阅读文件时使用此过滤器,您可以

List<String> oddWords = Files.lines(path).filter(WORDS_OF_ODD_LENGTH).collect(toList());

或者您可以在使用

加载它们时将它们编入索引
Map<Integer, List<String>> wordsBySize = Files.lines(path)
                                .collect(groupBy(s -> s.length()));

即使你已经使你的枚举成为谓词,你可以像这样优化它的用法。

if (predicate == WORDS_OF_ODD_LENGTH || predicate == WORDS_OF_EVEN_LENGTH) {
    // assume if the first word in a list of words of the same length
    // then take all words of that length.
    return wordsBySize.values().stream()
                               .filter(l -> predicate.test(l.get(0)))
                               .flatMap(l -> l.stream()).collect(toList());
} else {
    return wordsBySize.values().stream()
                               .flatMap(l -> l.stream())
                               .filter(predicate)
                               .collect(toList());
}

即。通过使用enum,您可以识别一些谓词并对其进行优化。 (这是否是个好主意我会留给你)