Как разделить Java List, чтобы получить один список с определенным условием, а другой со всеми пересекающимися? - PullRequest
0 голосов
/ 09 мая 2019

Как разделить Java List, чтобы получить следующие два типа списка:

  1. Список, содержащий элементы, удовлетворяющие определенному условию
  2. Список, содержащий все элементы, но они будут пересекаться друг с другом

Мой текущий рабочий подход использует forEach, как показано ниже:

Map<String, Set<Attribute>> applicableAttributeMap = new HashMap<>();

Set<Attribute> unionMandatoryAttributes = new HashSet<>();
Set<Attribute> intersectedAttributes = new HashSet<>();

givenNames.forEach(givenName -> {
    List<Attribute> applicableAttributes = getAllApplicableAttributes(givenName);  //function to retrieve List<Attribute> by passing givenName
    if (applicableAttributes != null && !applicableAttributes.isEmpty()) {
        unionMandatoryAttributes.addAll(
                applicableAttributes
                        .stream()
                        .filter(Attribute::getIsRequired)
                        .collect(Collectors.toSet())
        );

        if (intersectedAttributes.isEmpty()) {
            intersectedAttributes.addAll(applicableAttributes);
        }
        intersectedAttributes.retainAll(applicableAttributes);
    }
});

applicableAttributeMap.put(UnionMandatory, unionMandatoryAttributes);
applicableAttributeMap.put(IntersectedAll, intersectedAttributes);

Я пытаюсь упростить приведенный выше блок кода с помощью partitioningBy, но не могу получить желаемый результат. Я не могу собрать другой список, в котором есть все элементы вместе с Map s key как String.

Вот мой partitioningBy подход :

Map<Boolean, Set<Attribute>> applicableMap = givenNames
        .stream()
        .flatMap(s -> getAllApplicableAttributes(s).stream())
        .filter(Objects::nonNull)
        .collect(Collectors.partitioningBy(
                Attribute::getIsRequired,
                Collectors.mapping(Function.identity(),Collectors.toSet())
        ));

Как я могу создать Map<String , Set<Attributes>>, который будет удовлетворять условию, данному в рабочем подходе или любом другом упрощенном решении?

(Примечание: моя цель - добиться точного, что происходит при рабочем подходе Возможно, я что-то упустил при объяснении проблемы. Но суть в том, чтобы получить такой же результат, как и рабочий подход , используя что-то вроде partitioningBy или любой другой подход лучше, чем то, что я сделал.)

Ответы [ 2 ]

1 голос
/ 09 мая 2019

Двойная итерация по givenNames выглядит как лучший подход:

    Map<String, Set<Attribute>> applicableAttributeMap = new HashMap<>();

    List<String> givenNames = ImmutableList.copyOf(....);

    Set<Attribute> unionMandatoryAttributes =   
             givenNames.stream()
                       .map(this::getAllApplicableAttributes)
                       .flatMap(Collection::stream)
                       .filter(Attribute::getIsRequired)
                       .collect(Collectors.toSet());

    Set<Attribute> intersectedAttributes = ImmutableSet.of();
    if (givenNames.size() > 0) {
        intersectedAttributes = this.getAllApplicableAttributes(givesNames.get(0));         
        for (int i = 1; i < givenNames.size(); i++) {
             intersectedAttributes = Sets.intersection(intersectedAttributes, getAllApplicableAttributes(givesNames.get(i)));                           
        }
    }

    applicableAttributeMap.put("UnionMandatory", unionMandatoryAttributes);
    applicableAttributeMap.put("IntersectedAll", intersectedAttributes);
}

Обратите внимание, что в этом коде используются ImmutableList, ImmutableSet и Sets.intersection.

в Guava.Имейте в виду также удвоенное количество звонков на getAllApplicableAttributes в этом коде.

0 голосов
/ 09 мая 2019

Отредактировано после того, как комментарии указали, что я неправильно прочитал вопрос.

Мое исправленное решение:

На самом деле вы можете сделать пересечение с потоком по большей части, если вы просто инициализируете множество за пределами потока. Как это:

    Set<String> intersected = new HashSet<>();
    givenNames.stream()
            .map(gn ->  getAllApplicableAttributes(gn)) // yields Stream of List<Attribute> per given Name
            .forEach(list -> {
                if ( intersected.isEmpty() ){
                    intersected.addAll(list);
                } else {
                    intersected.removeIf(x -> !list.contains(x)); // remove attributes if not present in this iteration
                }
            });

Как уже упоминали другие люди, фильтрация имеет больше смысла, чем разбиение.

Set<Attribute>> unionMandatory = givenNames
        .stream()
        .flatMap(s -> getAllApplicableAttributes(s).stream())
        .filter(Attribute::getIsRequired)
        .collect(Collectors.toSet())

Поскольку intersected не может быть создан потоком, нет смысла создавать окончательную Карту из потока, поэтому я просто поместил бы оба этих набора в Карту, как вы это делали в своем рабочем подходе.

...