是否可以在单个Stream操作中进行拆分 - >进程 - >收集数据

问题描述 投票:0回答:2

我不想将流拆分为两个,但我想添加一个操作,在转换之前拆分数据。

为了说明这一点,假设我有一些共同的对象:

public class CommonItem {
    private String name;
    private boolean isSelected;
   /* getters and setters */
}

我有这些来代表多种不同的东西,如:

public class Foo {
    private String text;
    private boolean isChecked;

    public Foo(String text, boolean isChecked) {
        this.text = text;
        this.isChecked = isChecked;
    }
   /* getters and setters */
}

public class Bar {
    private String title;
    private boolean isTicked;

    public Bar(String title, boolean isTicked) {
        this.title = title;
        this.isTicked = isTicked;
    }
   /* getters and setters */
}

因此,在Stream操作中,我可以轻松地将它们转换为我想要的项目,然后通过boolean属性将它们拆分

listOfCommonItems.stream()
    .map(item -> new Foo(item.getName(), item.isSelected()))
    .collect(Collectors.groupingBy(Foo::isChecked));

这产生了我想要的输出 - 分成两个桶的Map<Boolean, List<Foo>> - 被检查的那些和不被检查的那些。但是,如果我想和Bar做同样的事情,我必须做一个不同的收集标准

listOfCommonItems.stream()
    .map(item -> new Bar(item.getName(), item.isSelected()))
    .collect(Collectors.groupingBy(Bar::isTicked));

得到Map<Boolean, List<Bar>>

我可以使用.filter然后我需要处理两次流。如果我将CommonItem分成两个并在之后处理这些结果,那就相似了。而且我并不需要两个流,因为它是相同的数据集,我只需要在两个桶中,其中共同标准在它们转换之前出现。

但是,我可以在映射之前以某种方式进行拆分,因此我可以轻松地为基于CommonItem的拆分创建一个逻辑,而不是最终转换项目的一个,然后在最后基于此标准收集?

java java-stream
2个回答
3
投票

如果我找对你,你想要这样的东西:

public static <T> Map<Boolean,List<T>> splitData(
    List<CommonItem> listOfCommonItems, BiFunction<String,Boolean,T> mapper) {

    return listOfCommonItems.stream()
        .collect(Collectors.partitioningBy(CommonItem::isSelected,
            Collectors.mapping(ci -> mapper.apply(ci.getName(), ci.isSelected()),
                Collectors.toList())));
}

可用作

Map<Boolean,List<Foo>> map1 = splitData(listOfCommonItems, Foo::new);
Map<Boolean,List<Bar>> map2 = splitData(listOfCommonItems, Bar::new);

你必须明白groupingBy(Function)partitioningBy(Predicate)groupingBy(Function, toList())的短手。 partitioningBy(Predicate, toList())。因此,如果要在将元素添加到列表之前插入其他操作(如mapping),则可以显式编写这些表单。

partitioningBy是一种用于布尔键的groupingBy的特殊形式,它允许底层实现在这种情况下使用优化代码。


要在一个Stream操作中执行此操作,您需要一个能够保存结果的类型。有一个类似的

class Both {
    List<Foo> foos = new ArrayList<>();
    List<Bar> bars = new ArrayList<>();
    void add(CommonItem ci) {
        String name = ci.getName();
        boolean sel = ci.isSelected();
        foos.add(new Foo(name, sel));
        bars.add(new Bar(name, sel));
    }
    Both merge(Both other) {
        if(foos.isEmpty()) return other;
        foos.addAll(other.foos);
        bars.addAll(other.bars);
        return this;
    }
}

你可以收集所有这些

Map<Boolean, Both> map = listOfCommonItems.stream()
    .collect(Collectors.partitioningBy(CommonItem::isSelected,
        Collector.of(Both::new, Both::add, Both::merge)));

但是,对于普通的List来说,避免遍历是没有优势的,所以这只是一个不必要的代码复杂化。


0
投票

这是我的两分钱。

List<CommonItem> listOfCommonItems = Arrays.asList(
    new CommonItem("foo", true),
    new CommonItem("bar", false),
    new CommonItem("bar", false),
    new CommonItem("foo", true),
    new CommonItem("foo", false),
    new CommonItem("bar", true),
    new CommonItem("bar", false)
);

Map<Class<?>, ? extends Map<Boolean, ? extends List<?>>> map = listOfCommonItems.stream()
    .map(item -> new SimpleEntry<>(item.isSelected(), convertCommonItem(item)))
    .collect(groupingBy(t -> t.getValue().getClass(), partitioningBy(
        Entry::getKey, mapping(t -> t.getValue(), toList()))));

    System.out.println(map);

每个类(FooBar,...)被映射到Partition对象,这是一个包含真假关键的Map。此类分区的每个值现在都包含一个列表,其中包含布尔属性相同的对象。

正如你在评论中所说的那样,bucketing的标记只是通过将bucketing标记(boolean)和转换后的对象包装成一对Pair(在我的例子中为AbstractMap.SimpleEntry)来实现。


这是转换的一些虚拟实现。我不知道你是如何计划将公共项转换为各自的类,但这是一个简单的实现,提供一些上下文:

private static Object convertCommonItem(CommonItem commonItem) {
    switch (commonItem.getName()) {
        case "foo":
            return new Foo(commonItem.getName(), commonItem.isSelected());
        case "bar":
            return new Bar(commonItem.getName(), commonItem.isSelected());
        default:
            throw new UnsupportedOperationException();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.