Lambda groupingBy Advanced question List < A > how to convert to List < B >

topic description

lambda groupingBy Advanced questions List < A > how to convert to List < B >

sources of topics and their own ideas

looked up a lot of materials and found that most of the Internet teaches how to convert List into Map < String,List

.

I know that the grupingBy method has three parameters
according to reason. If you define the output container B, and then define how to collect it, maybe you can achieve it?
I don"t know how to write it.

related codes

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

I hope to get this:

List<B>
[
    {
         "name":"name1"
         "values":[
             "1",
             "2"
         ]
    },
    {
         "name":"name2"
         "values":[
             "3",
             "4",
             "5"
         ]
    }    
]

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

Thank you for the invitation

according to the intuitive needs of the subject, I can give an affirmative answer to List < A > through groupingBy of stream to List < B > .

of course not.

because only groupingBy is used, its return, no matter how to use several parameters, can only be a map , which certainly cannot be transformed into a B , so it is necessary to use other methods in groupingBy . For example, after the brothers upstairs use groupingBy , the generated map is then looped with entrySet . If
loops the upstairs brothers

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

in that case, the upstairs brother's code can be written like this

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

to put it another way, of course, if the subject wants to finish it at once, he cannot call it done at once. It should be done in a different way, because from the point of view of requirements, the indispensable processing must be a classification operation, as well as a collection operation, but the off-the-shelf classification operation groupingBy will always return map . So we can learn the results returned by groupingBy to construct something similar to the groupingBy method

because the return result of groupingBy is a Collector , it is an interface, so we can customize an implementation class of this interface

clipboard.png

this interface, I inadvertently wrote a Collector small note introduced ha. If you are interested, you can take a look at my little understanding of it. For the reason of my time here, I will directly give the implementation class

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

in addition, B has added an extra constructor to the class

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

combined with the description of Collector interface in my previous notes, I believe the subject can simply understand this implementation. With such a collector, the operation will be easier

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

complete. Emmm, is like this, just for reference, just paste

Menu