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.
source to share
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);
source to share
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());
source to share