lambda groupingBy高级问题 List<A>怎么转换成List<B>

题目描述

lambda groupingBy高级问题 List<A>怎么转换成List<B>

题目来源及自己的思路

查了很多资料,发现大多数网上都是教怎么把List转换成Map<String,List>

我知道grupingBy方法有三个参数
按道理。。如果自己定义输出的容器B,然后再定义怎么收集,或许可以实现?
不知道怎么写...

相关代码

public class A{
  private String name;
  private String value;
}

public class B {
  private String name;
  private List<String> values;
}

List<A> list = new ArrayList<>();
list.add(new A("name1", "1"));
list.add(new A("name1", "2"));
list.add(new A("name2", "3"));            
list.add(new A("name2", "4"));
list.add(new A("name2", "5"));

希望得到这样:

List<B>
[
    {
         "name":"name1"
         "values":[
             "1",
             "2"
         ]
    },
    {
         "name":"name2"
         "values":[
             "3",
             "4",
             "5"
         ]
    }    
]
阅读 9.2k
3 个回答

谢邀

按照题主直观的问题需求来看,就是想要把List<A>通过streamgroupingBy怎么转换成List<B>,我可以给一个肯定答复。。。

当然是不可以鸭

因为只用groupingBy,它的返回,再怎么用几个参数,都只能是一个map,肯定转化不成不是个B,所以必须要在groupingBy外用其他方式,比如像楼上的兄弟就是用groupingBy之后,生成的map再用entrySet进行循环,如果
把楼上兄弟的构造方法写到B类里,估计看起来会好看点

public class B {
   private String name;
   private List<String> values;
        
   public B(Map.Entry<String, List<String>> entry){
       this.name = entry.getKey();
       this.values = entry.getValue();
   }
}

这样的话,楼上兄弟的代码就可以这么写了

List<B> bList = list.stream()
                    .collect(Collectors.groupingBy(A::getName, 
                                       Collectors.mapping(A::getValue, Collectors.toList())))
                    .entrySet()
                    .stream()
                    .map(B::new)
                    .collect(Collectors.toList());

当然换个说法来讲,若是题主想要一次性完成,也不能叫一次性完成把,应该是换种方式去实现的话,也是可以的,因为从需求来看,少不了的处理必定是归类操作,还有就是收集操作,但是现成的归类操作的方法groupingBy最终的返回对象永远都是map,所以我们可以来学groupingBy返回的结果来构造一个类似groupingBy方法的东西

因为groupingBy的返回结果是一个Collector,它是一个接口,所以我们就可以来定制一个该接口的实现类

clipboard.png

这个接口,之前我无意中写了一个Collector小笔记介绍了哈,感兴趣可以具体看看我对它的小理解,我这里时间原因,就直接给出实现类吧

public class BCollector implements Collector<A, List<B>, List<B>> {

    @Override
    public Supplier<List<B>> supplier() {
        return () -> new ArrayList<>();
    }

    @Override
    public BiConsumer<List<B>, A> accumulator() {
        // 这里只有我们自己来实现groupingBy的功能
        return (bs, a) -> {
            Optional<B> optionalB = bs.stream().filter(b -> b.getName().equals(a.getName())).findFirst();
            if (optionalB.isPresent()){
                B b = optionalB.get();
                List<String> values = b.getValues();
                if (values == null){
                    values = new ArrayList<>();
                }
                values.add(a.getValue());
                b.setValues(values);
            }else {
                B b = new B(a);
                bs.add(b);
            }
        };
    }

    @Override
    public BinaryOperator<List<B>> combiner() {
        return (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };
    }

    @Override
    public Function<List<B>, List<B>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
    }
}

另外B类里顺手多加了个构造方法

public B(A a){
     this.name = a.getName();
     this.values = Stream.of(a.getValue()).collect(Collectors.toList());
}

结合我之前小笔记对Collector接口的描述,相信题主能够简单了解这个实现,有了这么一个收集器之后,那操作就简单了

List<B> bList = list.stream().collect(new BCollector());

完成。。。emmm,就是这样吧,仅供参考,就酱

public static void main(String[] args) {

    List<AA> list = new ArrayList<AA>();
    list.add(new AA("name1", 1));
    list.add(new AA("name1", 2));
    list.add(new AA("name2", 3));
    list.add(new AA("name2", 4));
    list.add(new AA("name2", 5));

    List<B> listB = list.stream().collect(Collectors.groupingBy(new Function<AA, String>() {
        @Override
        public String apply(AA aa) {
            return aa.getName();
        }
    }, HashMap::new, Collectors.mapping(new Function<AA, Integer>() {
        @Override
        public Integer apply(AA aa) {
            return aa.getValue();
        }
    }, Collectors.toList()))).entrySet().stream()
            .map(new Function<Map.Entry<String, List<Integer>>, B>() {
                @Override
                public B apply(Map.Entry<String, List<Integer>> stringListEntry) {
                    return new B(stringListEntry.getKey(),stringListEntry.getValue());
                }
            }).collect(Collectors.toList());

    System.out.println(JSON.toJSONString(listB));
}
很久不写了,可能写的不太好,A和B的value我都改成Integer了
// 假设A中有x和y两个属性,该方法可以将将List<A> 分组转化为 Map<X, List<Y>>
public static <T, K, V> Map<K, List<V>> groupToMap(List<T> list, Function<T, K> keyExtractor, Function<T, V> valueExtractor){
    return list.stream().collect(Collectors.groupingBy(keyExtractor, Collectors.mapping(valueExtractor, Collectors.toList())));
}

// 假设A中有x和y两个属性,该方法可以将将List<A> 分组转化为 Tuple2<X, List<Y>>
public static <T, K, V> List<Tuple2<K, List<V>>> groupToTuple2(List<T> list, Function<T, K> keyExtractor, Function<T, V> valueExtractor){
    return groupToMap(list, keyExtractor, valueExtractor).entrySet().stream().map(e -> Tuple2.of(e.getKey(), e.getValue())).collect(Collectors.toList());
}

public static void main(String[] args) {
    List<A> list = Arrays.asList(new A("张三", 1), new A("李四", 2), new A("张三", 2), new A("王五", 3));

    /**
     * {"李四":[2],"张三":[1,2],"王五":[3]}
     */
    System.out.println(JSON.toJSONString(groupToMap(list, A::getName, A::getAge)));
    /**
     * [
     *    {"f0":"李四","f1":[2]},
     *    {"f0":"张三","f1":[1, 2]},
     *    {"f0":"王五","f1":[3]}
     * ]
     */
    System.out.println(JSON.toJSONString(groupToTuple2(list, A::getName, A::getAge)));
}
@AllArgsConstructor
@Data
public static class A {
    private String name;
    private Integer age;
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏