Java 8 use shrink and collect collectors to get list

EDIT ** Request for an answer to the first approach also using the pruning method **

 public class Messages {
  int id;
  String message;
  String field1;
  String field2;
  String field3;
  int audId;
  String audmessage;
 //constructor
   //getter or setters
 }


public class CustomMessage {

 int id;
 String msg;
 String field1;
 String field2;
 String field3;
 List<Aud> list;
 //getters and setters
}


  public class Aud {

    int id;
    String message;
   //getters and setters
   }


   public class Demo {

    public static  void main(String args[]){
      List<Messages> list = new ArrayList<Messages>();
      list.add(new Messages(1,"abc","c","d","f",10,"a1"));
      list.add(new Messages(2,"ac","d","d","f",21,"a2"));
      list.add(new Messages(3,"adc","s","d","f",31,"a3"));
      list.add(new Messages(4,"aec","g","d","f",40,"a4"));
      list.add(new Messages(1,"abc","c","d","f",11,"a5"));
      list.add(new Messages(2,"ac","d","d","f",22,"a5"));
     }

      

I want the message to be displayed with checks CustomMessage must have -> 1, "abc", "c", "d", "f" -----> List of 2 audits (10, a1) and (11, "a5 ");

There are two ways to do this

1.Reduce - I would like to use reduction also to create my own accumulator and combiner

   List<CustomMessage> list1= list.stream().reduce(new ArrayList<CustomMessage>(),
            accumulator1,
            combiner1);

    **I am unable to write a accumulator and combiner**

      

2.Collectors.groupingBy -

  • I don't want to use constructors to create the Message and neither for Custom Message .here I have fewer fields, my actual object has many fields. Any way to have a static method to create an object
  • Is there a way to do this by reducing by writing the accumulator or combiner

          List<CustomMessage> l = list.stream()
            .collect(Collectors.groupingBy(m -> new SimpleEntry<>(m.getId(), m.getMessage()),
                    Collectors.mapping(m -> new Aud(m.getAudId(), m.getAudMessage()), Collectors.toList())))
            .entrySet()
            .stream()
            .map(e -> new CustomMessage(e.getKey().getKey(), e.getKey().getValue(), e.getValue()))
            .collect(Collectors.toList());
    
          

Can anyone help me with both approaches.

+1


source to share


2 answers


This code will create Collection

of CustomMessage

. I would recommend putting a constructor on CustomMessage

that takes an argument Messages

. And maybe also move the mergeFunction from collect

.

Collection<CustomMessage> customMessages = list.stream()
        .collect(toMap(
                Messages::getId,
                m -> new CustomMessage(m.getId(), m.getMessage(), m.getField1(), m.getField2(), m.getField3(),
                        new ArrayList<>(singletonList(new Aud(m.getAudId(), m.getAudmessage())))),
                (m1, m2) -> {
                    m1.getList().addAll(m2.getList());
                    return m1;
                }))
        .values();

      

What's toMap here: The first time it is encountered Messages

id

, it puts it in the Map as a key with the newly created CustomMessage

second argument toMap

("valueMapper"). Next time it will merge the two CustomMessage

with the third argument, "mergeFunction", which effectively merges the 2 lists Aud

.



And if you absolutely need it List

, and not Collection

:

List<CustomMessage> lm = new ArrayList<>(customMessages);

      

+2


source


You cannot do this by grouping or shrinking. You need both: first the group and then shrink. I coded the reduction differently:

    List<CustomMessage> list1 = list.stream()
            .collect(Collectors.groupingBy(Messages::getId))
            .values()
            .stream() // stream of List<Messages>
            .map(lm -> {
                List<Aud> la = lm.stream()
                        .map(m -> new Aud(m.getAudId(), m.getAudmessage()))
                        .collect(Collectors.toList());
                Messages m0 = lm.get(0);
                return new CustomMessage(m0.getId(), m0.getMessage(), 
                        m0.getField1(), m0.getField2(), m0.getField3(), la);
            })
            .collect(Collectors.toList());

      

I injected a constructor in Aud

and then read your comment that you are trying to avoid constructors. I will come back to this question at the end. Anyway, you can rewrite object creation Aud

in the same way as in your question. And building objects CustomMessage

too, if you like.

Result:

[1 abc cdf [10 a1, 11 a5], 3 adc sdf [31 a3], 4 aec gdf [40 a4], 2 ac ddf [21 a2, 22 a5]]

I have grouped posts only by ID since you said their method equals

only uses ID. You can also group more fields as in your question. The fast and dirty road wold be

            .collect(Collectors.groupingBy(m -> "" + m.getId() + '-' + m.getMessage() 
                    + '-' + m.getField1() + '-' + m.getField2() + '-' + m.getField3()))

      

Avoiding public constructors and using static methods to create objects isn't much of a change. For example, if you have



public static Aud createAud(int id, String message) {
    return new Aud(id, message);
}

      

(well, that didn't completely destroy the constructor, but now you can declare it private, if still not satisfied, you can also rewrite the method to not use the declared constructor). Now in a stream, you just need to do:

                        .map(m -> Aud.createAud(m.getAudId(), m.getAudmessage()))

      

You can use the same way CustomMessage

. In this case, your static method can take an argument Messages

, if you like, as Manos Nikolaidis suggested, this might simplify your thread code a bit.

Edit . You couldn't just forget about the three-argument method reduce

, could you? ;-) It can be used. If you want to do this, I suggest you first fit CustomMessage

with a whole bunch of methods for this purpose:

private CustomMessage(int id, String msg, 
                      String field1, String field2, String field3, List<Aud> list) {
    this.id = id;
    this.msg = msg;
    this.field1 = field1;
    this.field2 = field2;
    this.field3 = field3;
    this.list = list;
}

public static CustomMessage create(Messages m, List<Aud> la) {
    return new CustomMessage(m.getId(), m.getMessage(), 
                             m.getField1(), m.getField2(), m.getField3(), la);
}

/**
 * @return original with the Aud from m added
 */
public static CustomMessage adopt(CustomMessage original, Messages m) {
    if (original.getId() != m.getId()) {
        throw new IllegalArgumentException("adopt(): incompatible messages, wrong ID");
    }
    Aud newAud = Aud.createAud(m.getAudId(), m.getAudmessage());
    original.addAud(newAud);
    return original;
}

public static CustomMessage merge(CustomMessage cm1, CustomMessage cm2) {
    if (cm1.getId() != cm2.getId()) {
        throw new IllegalArgumentException("Cannot merge non-matching custom messages, id "
                + cm1.getId() + " and " + cm2.getId());
    }
    cm1.addAuds(cm2.getList());
    return cm1;
}

private void addAud(Aud aud) {
    list.add(aud);
}

private void addAuds(List<Aud> list) {
    this.list.addAll(list);
}

      

It's not that bad with their help:

    List<CustomMessage> list2 = list.stream()
            .collect(Collectors.groupingBy(Messages::getId))
            .values()
            .stream()
            .map(lm -> lm.stream()
                        .reduce(CustomMessage.create(lm.get(0), new ArrayList<>()),
                                CustomMessage::adopt, 
                                CustomMessage::merge))
            .collect(Collectors.toList());

      

+1


source







All Articles