【问题标题】:Stream api to spilt ArrayList in batches based on conditionsStream api根据条件分批拆分ArrayList
【发布时间】:2021-10-15 19:12:34
【问题描述】:

给定以下示例种子:

    List<EmailGroupingDataDTO> emailDataToGroup = Arrays.asList(
            buildEmailGroupingDto(1L, "member_number_1", "username_1", "group_code_1"),
            buildEmailGroupingDto(2L, "member_number_1", "username_1", "group_code_1"),
            buildEmailGroupingDto(3L, "member_number_1", "username_2", "group_code_1"),
            buildEmailGroupingDto(4L, "member_number_1", "username_2", "group_code_1"),
            buildEmailGroupingDto(5L, "member_number_1", "username_2", "group_code_3"),
            buildEmailGroupingDto(6L, "member_number_2", "username_1", "group_code_1"),
            buildEmailGroupingDto(7L, "member_number_2", "username_1", "group_code_2"),
            buildEmailGroupingDto(8L, "member_number_2", "username_1", "group_code_2"),
            buildEmailGroupingDto(9L, "member_number_3", "username_1", "group_code_1"),
            buildEmailGroupingDto(10L, "member_number_3", "username_1", "group_code_1"),
            buildEmailGroupingDto(11L, "member_number_3", "username_1", "group_code_2"),
            buildEmailGroupingDto(12L, "member_number_3", "username_1", "group_code_2"),
            buildEmailGroupingDto(13L, "member_number_3", "username_1", "group_code_3"),
            buildEmailGroupingDto(14L, "member_number_4", "username_1", "group_code_3"),
            buildEmailGroupingDto(15L, "member_number_4", "username_1", "group_code_3")
    );

我想根据以下任一属性在遍历列表时是否发生变化来创建列表(批次):

private boolean isEmailDataEqual(EmailGroupingDataDTO currDto, EmailGroupingDataDTO nextDto) {
    return currDto.getMemberNumber().equals(nextDto.getMemberNumber())
        && currDto.getUsername().equals(nextDto.getGroupCode())
        && currDto.getGroupCode().equals(nextDto.getGroupCode());
}

通过定期迭代,我可能可以自己管理这个。
但是我的问题是是否有一个流 api 可以声明式地管理它。

【问题讨论】:

  • 你试过 Collectors.toMap 了吗??
  • @Antoniossss 每个属性更改都应该迭代地触发新列表的开始。 IE。这不是常规聚合。
  • 这大部分都可以通过一组嵌套的flatMaps 来实现,但是如果不使用库或其他东西,一开始的索引会使它变得更加困难。
  • @LouisWasserman 感谢您的合理解释。我只是在写一个迭代器;希望有一个简单的声明性流api,但没关系。顺便说一句,这些索引只是属性。它只不过是一个隐藏在工厂方法后面的构造函数,用于跳过单元测试不需要的字段。
  • @html_programmer 您可以拥有完全可以做到这一点的全状态收集器。它是简单的聚合——它只是通过比较当前和以前的元素生成的键。我认为在非并行流中,顺序是有保证的,因此它应该可以工作。

标签: java java-8


【解决方案1】:

StreamEx 会为你做这件事:

List<List<EmailGroupingDataDTO>> groups = StreamEx.of(emailDataToGroup)
        .groupRuns(this::isEmailDataEqual)
        .toList();

【讨论】:

  • 赞成这个,因为它可能有效。不过还没有尝试过,因为我不会为这个简单的东西添加一个完整的库。通过定期迭代解决了这个问题。
  • 如果事实证明我们在批处理过程中的其他地方需要此方法和其他方法,可能值得添加库,我将对此进行审查。
【解决方案2】:

您可以将AtomicInteger &amp; AtomicReference 用于Collectors.groupingBy 的分类器并构建地图(并使用Map.values 获取分组批次)。整数作为组键,引用作为组的代表元素,并为您的属性提供一种简单的比较方法。

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

....


List<EmailGroupingDataDTO> emailDataToGroup = //your list 

AtomicInteger ai = new AtomicInteger();
AtomicReference<EmailGroupingDataDTO> myRef = new AtomicReference<>(emailDataToGroup.get(0));

Collection<List<EmailGroupingDataDTO>> result =  
        emailDataToGroup.stream().collect(Collectors.groupingBy(myDto -> {
            boolean equal = isEmailDataEqual(myDto, myRef.get());
            if (!equal) myRef.getAndSet(myDto);
            return equal ? ai.get() : ai.incrementAndGet();
        })).values();

注意:正如@shmosel 正确指出的那样,这种方法只有在您按顺序流式传输列表时才有效。

【讨论】:

  • 这是有状态的,不会并行工作。
  • 当然这是一种创造性的简化方式。
  • @shmosel 感谢您的提示。我将添加相应的注释。由于这里只有一个答案,至少到目前为止,它指向外部依赖项,因此 OP 可能对适合顺序流的解决方案感到满意,特别是因为他/她没有明确寻找也适合的解决方案用于并行流。
猜你喜欢
  • 2017-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-17
  • 2010-10-31
  • 1970-01-01
  • 2018-03-03
  • 2020-03-20
相关资源
最近更新 更多