Не в состоянии фильтровать элементы, используя перечисление - PullRequest
0 голосов
/ 19 февраля 2020
protected static double averagePrice( List<ComputerComponent> list ) { 
    return  list.stream()
            //  .filter( line-> EnumUtils.isValidEnum(ComputerComponentCategory.class, line.getCategory()) )    

            //  .filter( line-> isInEnum( line.getCategory(), ComputerComponentCategory.class) )    
            //   .filter( line->  inEnum(line.getCategory(),EnumUtils.getEnumMap(ComputerComponentCategory.class ).keySet() ))  
                .filter(    line -> 
                        line.getCategory().contains("CPU")  
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory")) 
               .mapToDouble(ComputerComponent::getPrice)
               .average() 
               .orElseThrow(NoSuchElementException:: new); 
    }

У меня есть перечисление как

public enum ComputerComponentCategory {

    CPU("CPU"), 
    MONITOR("Monitor"), 
    KEYBOARD("Keyboard"), 
    MOUSE("Mouse"), 
    GPU("GPU"), 
    MEMORY("Memory"),
    STORAGE("Storage"),
    NULL("NOT DEFINED"); 

    private String label;

    ComputerComponentCategory(String label) {
        this.label = label;
    }

    public String getLabel() {
        return this.label;
    }

    public static ComputerComponentCategory getValue(String label) {

        switch(label) {
            case "CPU":
                return CPU;
            case "Monitor":
                return MONITOR;
            case "Keyboard":
                return KEYBOARD;
            case "Mouse":
                return MOUSE;
            case "GPU":
                return GPU;
            case "Memory":
                return MEMORY;
            case "Storage":
                return STORAGE;
            default: 
                return NULL ;
        }

    }
}

Я передаю список класса ComputerComponent в функцию averagePrice (), в которой есть два поля цены типа double и категория типа Строка.

В моем списке 4 элемента с категориями «ЦП», «Мышь», «Клавиатура» и «Хранилище» с соответствующими ценами 34,0, 155,0, 23,0 и 75,0.

Когда я пытаюсь использовать функции inEnum (), isInEnum () или EnumUtils.isValidEnum (), я получаю среднюю цену как 34.0, которая, я думаю, что они просто возвращают цену первого элемента, а не среднюю ,

But when I do filtering using 

                            .filter(    line -> 
                        line.getCategory().contains("CPU")  
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory"))

Я получаю правильное среднее значение 71,75.

Реализации, которые я использовал для функций isInEnum () и inEnum (), следующие:

public static <E extends Enum<E>> boolean isInEnum(String value, Class<E> enumClass) {
      for (E e : enumClass.getEnumConstants()) {
        if(e.name().contains(value)) { return true; }
      }
      return false;
    }


public static boolean inEnum ( String category, Set<String> value ) {   
    for(String s: value ) {
    if ( category.contains(s) ) {
        return true ;  
    }
    }
    return false ;
}

Как правильно использовать перечисления с java потоками для фильтрации по допустимой категории имена и получить правильное среднее значение цены?

Какую ошибку я допускаю при использовании потоков и их функций?

1 Ответ

1 голос
/ 19 февраля 2020

Вы можете просто использовать свой метод ComputerCategoryValue.getValue и проверить на ноль, учитывая категорию line:

public class EnumTest {

    @Test
    public void testBothMethods() {
        final ComputerComponent c1 = new ComputerComponent(ComputerComponentCategory.CPU.getLabel(), 12.21);
        final ComputerComponent c2 = new ComputerComponent(ComputerComponentCategory.MEMORY.getLabel(), 23.45);
        final List<ComputerComponent> list = Arrays.asList(c1, c2);

        assertEquals(averagePriceWithFilter(list), averagePriceWithInEnum(list), 0.01);
    }

    protected static double averagePriceWithFilter(final List<ComputerComponent> list) {
        return list.stream()
                .filter(line -> line.getCategory().contains("CPU")
                        || line.getCategory().contains("GPU")
                        || line.getCategory().contains("Monitor")
                        || line.getCategory().contains("Keyboard")
                        || line.getCategory().contains("Mouse")
                        || line.getCategory().contains("Storage")
                        || line.getCategory().contains("Memory"))
                .mapToDouble(ComputerComponent::getPrice)
                .average()
                .orElseThrow(NoSuchElementException::new);
    }

    protected static double averagePriceWithInEnum(final List<ComputerComponent> list) {
        return list.stream()
                .filter(line -> ComputerComponentCategory.getValue(line.getCategory()) != null)
                .mapToDouble(ComputerComponent::getPrice)
                .average()
                .orElseThrow(NoSuchElementException::new);
    }
}

РЕДАКТИРОВАТЬ: объясняя свои ошибки:

  1. EnumUtils.getEnumMap(ComputerComponentCategory.class).keySet()) возвращает карту имени перечисления (не * label), так что проверка будет работать только для CPU, поскольку там имя и метка совпадают.

  2. То же самое для другого метода!

Вам нужно использовать getLabel() вместо name() или equalsIgnoreCase вместо contains.

...