我有一个项目,在不同的场景中,我必须处理大型数据集的不同子集.我编写代码的方式有一个Collector接口,一个DataCollector类实现了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);
有没有办法对我的枚举进行建模以进行此类操作?我对其他想法持开放态度(因为我应该废弃这种基于枚举的方法来解决其他问题等).
解决方法
我建议你使用Java 8,它有Predicate和支持谓词的操作.
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()); }
即通过使用枚举,您可以识别一些谓词并对其进行优化. (这是不是一个好主意我会留给你)