Reputation: 518
I have Complex List of elements. Each inner list contains 3 fields: Code, Name, Version. I need to get final list of List which has max version numbers for each code.
Below code snippet working as expected but looking for effective solution.
List<List<Object>> lists = new ArrayList<>();
List<List<Object>> finalList = new ArrayList<>();
// each list contains => code, name, version
lists.add(Arrays.asList("code1", "Name1", 1));
lists.add(Arrays.asList("code1", "Name1", 2));
lists.add(Arrays.asList("code1", "Name1", 3));
lists.add(Arrays.asList("code1", "Name1", 3));
lists.add(Arrays.asList("code2", "Name2", 1));
lists.add(Arrays.asList("code2", "Name2", 2));
// get all uniqueCodes
List<Object> uniqueCodes = lists.stream().map(row -> row.get(0)).distinct().collect(Collectors.toList());
// iterate over uniqueCodes
uniqueCodes.stream().forEach(code -> {
// get lists for this uniqueCode
Supplier<Stream<List<Object>>> sameCodeLists = () -> lists.stream().filter(row -> row.get(0) == code);
// get max version of this uniqueCode
int maxVersion = sameCodeLists.get().mapToInt(row -> (int) row.get(2)).max().getAsInt();
// for this uniqueCode, get all Lists containing Max Version
finalList.addAll(sameCodeLists.get().filter(row -> (int) row.get(2) == maxVersion && row.get(0) == code).collect(Collectors.toList()));
});
System.out.println(finalList);
Input: [[code1, Name1, 1], [code1, Name1, 2], [code1, Name1, 3], [code1, Name1, 3], [code2, Name2, 1], [code2, Name2, 2]] Output: [[code1, Name1, 3], [code1, Name1, 3], [code2, Name2, 2]]
This is appropriate and as expected. Looking for code optimization to finish the job with fewer iterations.
Upvotes: 0
Views: 96
Reputation: 49646
You could greatly simplify it by
List<Object>
s; Collectors.groupingBy
and Collectors.maxBy
.@Getter
@ToString
@RequiredArgsConstructor
public final class Entity {
private final String code;
private final String name;
private final int version;
public static void main(String[] args) {
final List<Entity> entities = Arrays.asList(
new Entity("code1", "Name1", 1),
new Entity("code1", "Name1", 2),
new Entity("code1", "Name1", 3),
new Entity("code1", "Name1", 3),
new Entity("code2", "Name2", 1),
new Entity("code2", "Name2", 2)
);
final Map<String, Optional<Entity>> result = entities.stream()
.collect(Collectors.groupingBy(Entity::getCode,
Collectors.maxBy(Comparator.comparingInt(Entity::getVersion))));
System.out.println(result);
// {
// code2=Optional[Entity(code=code2, name=Name2, version=2)],
// code1=Optional[Entity(code=code1, name=Name1, version=3)]
// }
}
}
As @Aomine suggested in the comments, Collectors.collectingAndThen
with a finisher function might be used to map it to the final result.
Map<String, Optional<Integer>> result = entities.stream()
.collect(groupingBy(Entity::getCode,
collectingAndThen(maxBy(Comparator.comparingInt(Entity::getVersion)),
opEntity -> opEntity.map(Entity::getVersion))));
System.out.println(result);
// {code2=Optional[2], code1=Optional[3]}
It's not a trivial task, so Stream API may not be a good fit. Nevertheless, I came up with the following approach which seems to do the job.
List<Entity> result = entities.stream()
// group by code and sort the resulting lists by version
.collect(groupingBy(Entity::getCode, HashMap::new,
collectingAndThen(toList(), e -> e.stream().sorted(comparingInt(Entity::getVersion).reversed()).collect(toList()))))
.values()
.stream()
// keep only the max values
.map(l -> l.size() > 0 ? l.stream().filter(i -> i.getVersion() == l.get(0).getVersion()).collect(toList()) : l)
.flatMap(List::stream)
.collect(toList());
System.out.println(result);
// [
// Entity(code=code2, name=Name2, version=2),
// Entity(code=code1, name=Name1, version=3),
// Entity(code=code1, name=Name1, version=3)
// ]
Upvotes: 2