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@
