如何对Java列表进行分区以获取一个具有特定条件的列表,而其他却被所有相交?

时间:2019-05-09 15:37:34

标签: java java-8 java-stream

如何对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的{​​{1}}作为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>>之类的方法或比我做的更好的方法。)

2 个答案:

答案 0 :(得分:1)

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

请注意,此代码依赖于番石榴的ImmutableListImmutableSetSets.intersection

还要注意此代码中对getAllApplicableAttributes的调用次数已翻倍。

答案 1 :(得分:-1)

在评论指出我误解了问题后进行了编辑。

我修改后的解决方案:

实际上,如果只在Set之外初始化Set,则实际上可以与Stream进行交集。像这样:

    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不能由Stream创建,因此从Stream创建最终Map没有意义,所以我将像在工作方法中一样将这两个集合都放入Map中。