Logical operations on Java enumerations
I have a project where I have to deal with different subsets of large data sets in different scenarios The way I write code has a collector interface, and a DataCollector class implements the collector The DataCollector class instantiates conditions created using subsets, which are enumerations
Assuming that the data set is a set of 1 million English words, I want to study a subset of words composed of odd letters Then I do the following: @ h_ 403_ 3@
DataCollector dataCollector = new DataCollector(CollectionType.WORDS_OF_ODD_LENGTH); Set<String> fourLetteredWords = dataCollector.collect();
Where collectiontype is the enumeration class @ H_ 403_ 3@
enum CollectionType { WORDS_OF_ODD_LENGTH,WORDS_OF_EVEN_LENGTH,STARTING_WITH_VOWEL,STARTING_WITH_CONSONANT,.... }
The data collector calls Java. Net based on the instantiated enumeration util. Predicate.@ H_ 403_ 3@
So far, this method has been powerful and flexible enough, but now I am facing more and more complex scenes (for example, collecting words of uniform length from vowels) I want to avoid adding a new collectiontype for each such scenario What I notice is that many of these complex scenarios are only logical operations on simpler scenarios (for example, condition_1 & & (condition_2 | condition_3))@ H_ 403_ 3@
The end user is the person who specifies these conditions, and my only control is that I can specify a set of these conditions Similarly, the end user can only select from the collectiontype Now, I try to extend the ability to select only one condition to the ability to select one or more For this, I need something similar @ H_ 403_ 3@
DataCollector dataCollector = new DataCollector(WORDS_OF_ODD_LENGTH && STARTING_WITH_VOWEL);
Is there a way to model my enumeration for such operations? I am open to other ideas (because I should abandon this enumeration based method to solve other problems, etc.)@ H_ 403_ 3@
Solution
I suggest you use Java 8, which has predicate and predicate operations
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 } }
You can write @ h like a predicate_ 403_ 3@
Predicate<String> p = WORDS_OF_ODD_LENGTH.and(STARTING_WITH_CONSONANT);
Even the five letter word @ h that begins with a vowel_ 403_ 3@
Predicate<String> p = STARTING_WITH_VOWEL.and(s -> s.length() == 5);
Suppose you want to use this filter when reading files, you can do @ H_ 403_ 3@
List<String> oddWords = Files.lines(path).filter(WORDS_OF_ODD_LENGTH).collect(toList());
Or you can index them at load time @ H_ 403_ 3@
Map<Integer,List<String>> wordsBySize = Files.lines(path) .collect(groupBy(s -> s.length()));
Even if you have made your enumeration a predicate, you can optimize its usage like this@ H_ 403_ 3@
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()); }
That is, by using enumeration, you can identify and optimize some predicates (is this a good idea? I'll leave it to you) @ h_ 403_ 3@