代码之家  ›  专栏  ›  技术社区  ›  VLAZ Sohail Arif

是否可以在单个流操作中使用split->process->collect data

  •  0
  • VLAZ Sohail Arif  · 技术社区  · 5 年前

    我不想把一个流分成两个,但我想添加一个将数据拆分的操作 之前 我改变了它。

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

    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 */
    }
    

    因此,在流操作中,我可以很容易地将它们转换为我想要的项,并通过执行

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

    这会产生我想要的输出-a 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 分成两部分,然后处理这些结果。我不需要两个流,因为它是同一组数据,我只需要在两个bucket中使用它,在它们的转换之前,公共条件就在这里。

    但是,我能不能把它分开 之前 映射,因此我可以很容易地为基于 普通项目 而不是一个为最终转换的项目,然后根据这个条件收集在最后?

    0 回复  |  直到 5 年前
        1
  •  3
  •   Holger    5 年前

    如果我没弄错,你想要这样的东西:

    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()) RESP partitioningBy(Predicate, toList()) . 因此,如果要插入其他操作,如 mapping 在将元素添加到列表之前。

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


    要在一个流操作中完成此操作,您需要一个能够保存结果的类型。像这样的班级

    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 ,避免遍历没有任何好处,因此这只是不必要的代码复杂化。

        2
  •  0
  •   MC Emperor    5 年前

    这是我的两分钱。

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

    每堂课( Foo , Bar ,…)映射到 Partition 对象,它是 Map 包含密钥 . 这样的分区的每个值现在都包含一个列表,其中包含布尔属性相同的对象。

    bucketing的标记,如您在注释中所称,是通过将bucketing标记(布尔值)和转换后的对象包装成一对来实现的( 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();
        }
    }