Logical operations with Java enumerations

I have a project where in different scenarios I have to work with different subsets of a large data set. As I wrote the code, there is a Collector interface and a DataCollector implements Collector class. The DataCollector class is created with the condition of creating a subset, and these conditions are enumerations.

Let's say a data set is a set of 1 million English words, and I want to work on a subset of words consisting of an odd number of letters. Then I do the following:

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

where CollectionType is an enumeration class

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

The data collector calls java.util.Predicate depending on the enumeration with which it was created.

Until now, this approach has been quite strong and flexible, but now I come across increasingly complex scenarios (for example, they collect words of even length, starting with a vowel). I would like to avoid adding a new CollectionType for each such scenario. I noticed that many of these complex scenarios are just logical operations on simpler ones (for example, condition_1 && (condition_2 || condition_3) ).

The end user is the one who sets these conditions, and the only control I have is that I can specify a set of such conditions. As with the case, the end user can only select CollectionType . Right now I'm trying to generalize the possibility of choosing only one condition for the possibility of choosing one or more. For this I need something like

 DataCollector dataCollector = new DataCollector(WORDS_OF_ODD_LENGTH && STARTING_WITH_VOWEL); 

Is there a way that I model my enumerations to perform such operations? I am open to other ideas (like, I should just abandon this enum-based approach for something else, etc.).

+6
source share
1 answer

I suggest you use Java 8 with a predicate and operations that support predicates.

 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 Predicate as

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

or even five words of letters starting with a vowel

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

Suppose you want to use this filter when reading a file, you can do

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

Or you can index them at boot using

 Map<Integer, List<String>> wordsBySize = Files.lines(path) .collect(groupBy(s -> s.length())); 

Although you made your Predicate enumerator, you can optimize its use as follows.

 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()); } 

i.e. using enum , you can recognize some predicates and optimize them. (Whether this is a good idea or not, I will leave you)

+11
source

Source: https://habr.com/ru/post/981843/


All Articles