从具有相同成员的列表中筛选对象

时间:2019-02-22 13:58:10

标签: java functional-programming java-stream vavr

我有一个对象列表。该对象看起来像这样:

public class Slots {
  String slotType;
  Visits visit;
}


public class Visits {
  private long visitCode;
  private String agendaCode;
  private String scheduledTime;
  private String resourceType;
  private String resourceDescription;
  private String visitTypeCode;
  ...
}

我需要找到具有相同的agendaCodevisitTypeCodescheduledTime的元素,在我的生命中,我无法完成它。

我尝试过:

Set<String> agendas = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getAgendaCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> visitTypeCode = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getVisitTypeCode)
    .collect(Collectors.toUnmodifiableSet());

Set<String> scheduledTime = slotsResponse.getContent().stream()
    .map(Slots::getVisit)
    .map(Visits::getScheduledTime)
    .collect(Collectors.toUnmodifiableSet());

List<Slots> collect = slotsResponse.getContent().stream()
    .filter(c -> agendas.contains(c.getVisit().getAgendaCode()))
    .filter(c -> visitTypeCode.contains(c.getVisit().getVisitTypeCode()))
    .filter(c -> scheduledTime.contains(c.getVisit().getScheduledTime()))
    .collect(Collectors.toList());

但是它没有按照我的预期去做。理想情况下,我将有一个列表列表,其中每个子列表都是共享相同的agendaCodevisitTypeCodescheduledTime的Slots对象的列表。我在函数式编程中苦苦挣扎,因此任何帮助或指针都将非常有用!

这是Java 11,我也在使用vavr。

4 个答案:

答案 0 :(得分:3)

由于您提到您正在使用vavr,因此这是解决此问题的vavr方法。

假设您有io.vavr.collection.List(或ArrayVectorStream或类似的vavr集合)的访问:

List<Visits> visits = ...;

final Map<Tuple3<String, String, String>, List<Visits>> grouped =
    visits.groupBy(visit ->
        Tuple.of(
            visit.getAgendaCode(),
            visit.getVisitTypeCode(),
            visit.getScheduledTime()
        )
    );

或进行java.util.List次访问:

List<Visits> visits = ...;

Map<Tuple3<String, String, String>, List<Visits>> grouped = visits.stream().collect(
    Collectors.groupingBy(
        visit ->
            Tuple.of(
                visit.getAgendaCode(),
                visit.getVisitTypeCode(),
                visit.getScheduledTime()
            )
    )
);

答案 1 :(得分:1)

最简单的方法是使用必需字段(agendaCodevisitTypeCodescheduledTime)定义一个新类。不要忘了equals/hashcode

public class Visits {
    private long visitCode;
    private String resourceType;
    private String resourceDescription;
    private Code code;
    ...
}
class Code {
    private String agendaCode;
    private String scheduledTime;
    private String visitTypeCode;
    ...
    @Override
    public boolean equals(Object o) {...}
    @Override
    public int hashCode() {...}
}

然后,您可以使用groupingBy,例如:

Map<Code, List<Slots>> map = slotsResponse.getContent().stream()
            .collect(Collectors.groupingBy(s -> s.getVisit().getCode()));

此外,您仅可以在equals内为VisitsagendaCodevisitTypeCode实现scheduledTime方法。在这种情况下,请使用groupingBy的{​​{1}}

答案 2 :(得分:1)

我爱Ruslan's idea of using Collectors::groupingBy。不过,我不喜欢创建新类或定义新的equals方法。它们都将您强制为单个Collectors::groupingBy版本。如果要按其他方法按其他字段分组怎么办?

这是一段代码,应该可以让您克服这个问题:

slotsResponse.getContent()
    .stream()
    .collect(Collectors.groupingBy(s -> Arrays.asList(s.getVisit().getAgendaCode(), s.getVisit().getVisitTypeCode(), s.getVisit().getScheduledTime())))
    .values();

我的想法是为每个需要的字段(agendaCode,visitTypeCode,scheludedTime)创建一个新容器,并比较这些新创建的容器上的广告位。我本来希望使用一个简单的Object数组来完成此操作,但是它不起作用-应该将数组与Arrays.equals进行比较,这不是Collectors::groupingBy使用的比较方法。

请注意,您应该将其存储在某个地方或使用一种方法来定义要分组的字段。

答案 3 :(得分:1)

您要分组的字段都是字符串。您可以定义一个将这些字段值串联起来的函数,并将其用作组的键。例子

Function<Slots,String> myFunc = s -> s.getVisit().agendaCode + s.getVisit().visitTypeCode + s.getVisit().scheduledTime;
                               // or s.getVisit().agendaCode +"-"+ s..getVisit().visitTypeCode +"-"+ s.getVisit().scheduledTime;

然后按如下所示进行分组:

Map<String,List<Slots>> result = slotsResponse.getContent().stream()
                               .collect(Collectors.groupingBy(myFunc));