У меня есть два списка (скажем, A и B) одного типа "MyInfoObject", такие что:
public class MyInfoObject {
private Long id;
private String signature;
public MyInfoObject(Long id, String signature) {
super();
this.id = id;
this.signature = signature;
}
}
Я хочу создать Карту из этих двух списков так, чтобы все идентификаторы списка A и все идентификаторы списка B, имеющие одинаковую подпись, создавали сегмент типа "BucketOfAandB":
public class BucketOfAandB {
private List<Long> aIds ;
private List<Long> bIds ;
public BucketOfAandB(List<Long> aIds, List<Long> bIds) {
super();
this.aIds = aIds;
this.bIds = bIds;
}
}
Итак, мой вывод будет Map<String, BucketOfAandB>
, где ключ является подписью
Например, мой вклад:
List<MyInfoObject> aList = new ArrayList<>();
aList.add(new MyInfoObject(1l, "a"));
aList.add(new MyInfoObject(2l, "d"));
aList.add(new MyInfoObject(3l, "b"));
aList.add(new MyInfoObject(4l, "a"));
aList.add(new MyInfoObject(5l, "a"));
aList.add(new MyInfoObject(6l, "c"));
aList.add(new MyInfoObject(7l, "a"));
aList.add(new MyInfoObject(8l, "c"));
aList.add(new MyInfoObject(9l, "b"));
aList.add(new MyInfoObject(10l, "d"));
List<MyInfoObject> bList = new ArrayList<>();
bList.add(new MyInfoObject(11l, "a"));
bList.add(new MyInfoObject(21l, "e"));
bList.add(new MyInfoObject(31l, "b"));
bList.add(new MyInfoObject(41l, "a"));
bList.add(new MyInfoObject(51l, "a"));
bList.add(new MyInfoObject(61l, "c"));
bList.add(new MyInfoObject(71l, "a"));
bList.add(new MyInfoObject(81l, "c"));
bList.add(new MyInfoObject(91l, "b"));
bList.add(new MyInfoObject(101l, "e"));
Мой вывод в этом случае будет:
{
a= BucketOfAandB[aIds=[1, 4, 5, 7], bIds=[11, 41, 51, 71]],
b= BucketOfAandB[aIds=[3, 9], bIds=[31, 91]],
c= BucketOfAandB[aIds=[6, 8], bIds=[61, 81]],
d= BucketOfAandB[aIds=[2, 10], bIds=null],
e= BucketOfAandB[aIds=null, bIds=[21, 101]],
}
Я хочу сделать это, используя потоки Java 8.
Один способ, который я понял, был:
- создать
Map<String, List<Long>>
изaList
, скажем, aBuckets - итерируйте bList и создайте
resultant
Map<String, BucketOfAandB>
< UL> < Литий> 2а . установив List из aBuckets с такой же подписью для результирующего, удалите его изaBuckets
< Li> 2b . добавление элемента - перебрать все оставшиеся элементы
aBuckets
и добавить их вresultant
bList
в требуемый сегмент подписи Я хочу знать лучший способ реализовать это с помощью Streams of Java 8.
Заранее спасибо!
РЕДАКТИРОВАТЬ: я пытался использовать поток, но не очень доволен реализацией. Следующее - моя логика:
Map<String, BucketOfAandB> resultmap = new HashMap<>();
// get ids from aList grouped by signature
Map<String, List<Long>> aBuckets = aList.stream().collect(Collectors.groupingBy(MyInfoObject::getSignature,
Collectors.mapping(MyInfoObject::getId, Collectors.toList())));
// iterate bList and add it to bucket of its signature
bList.forEach(reviewInfo -> {
BucketOfAandB bucket = resultmap.get(reviewInfo.getSignature());
if(null == bucket) {
bucket = new BucketOfAandB();
resultmap.put(reviewInfo.getSignature(), bucket);
List<Long> sourceReviewBucket = aBuckets.remove(reviewInfo.getSignature());
if(null !=sourceReviewBucket) {
bucket.setaIds(sourceReviewBucket);
}
}
bucket.addToB(reviewInfo.getId());
});
Map<String, BucketOfAandB> result = aBuckets.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> new BucketOfAandB(e.getValue(), null)));
resultmap.putAll(result);
4 ответа
Если вы добавляете геттеры в MyInfoObject
и ленивые BucketOfAandB
инициализируют его списки (то есть без конструктора) следующим образом:
public class BucketOfAandB {
private List<Long> aIds;
private List<Long> bIds;
public void addAId(Long id) {
if (aIds == null) {
aIds = new ArrayList<>();
}
aIds.add(id);
}
public void addBId(Long id) {
if (bIds == null) {
bIds = new ArrayList<>();
}
bIds.add(id);
}
}
Вы можете сделать это всего за 3 строки, сохраняя семантику своего намерения:
Map<String, BucketOfAandB> map = new HashMap<>();
aList.forEach(o -> map.computeIfAbsent(o.getSignature(), s -> new BucketOfAandB())
.addAId(o.getId()));
bList.forEach(o -> map.computeIfAbsent(o.getSignature(), s -> new BucketOfAandB())
.addBId(o.getId()));
Если вы используете параллельные потоки, synchronize
методы add, которые практически не прибавят производительности, поскольку это только потенциальная коллизия в корзине.
Как насчет этого:
Map<String, List<Long>> mapA = aList.stream()
.collect(Collectors.groupingBy(
MyInfoObject::getSignature,
Collectors.mapping(MyInfoObject::getId, Collectors.toList())));
Map<String, List<Long>> mapB = bList.stream()
.collect(Collectors.groupingBy(
MyInfoObject::getSignature,
Collectors.mapping(MyInfoObject::getId, Collectors.toList())));
Map<String, BucketOfAandB> overAll = new HashMap<>();
Set<String> allKeys = new HashSet<>();
allKeys.addAll(mapA.keySet());
allKeys.addAll(mapB.keySet());
allKeys.forEach(x -> overAll.put(x, new BucketOfAandB(mapA.get(x), mapB.get(x))));
Но это предполагает, что каждый ключ, присутствующий в listA
, будет присутствовать в listB
Вы могли бы написать что-то вроде:
Function<List<MyInfoObject>, Map<String, List<Long>>> toLongMap =
list -> list.stream()
.collect(groupingBy(MyInfoObject::getSignature,
mapping(MyInfoObject::getId, toList())));
Map<String, List<Long>> aMap = toLongMap.apply(aList);
Map<String, List<Long>> bMap = toLongMap.apply(bList);
Map<String, BucketOfAandB> finalMap = new HashMap<>();
aMap.forEach((sign, listA) -> {
finalMap.put(sign, new BucketOfAandB(listA, bMap.get(sign)));
});
bMap.forEach((sign, listB) -> {
finalMap.putIfAbsent(sign, new BucketOfAandB(null, listB));
});
Как вы сказали, сначала вы можете создать Map<String, List<Long>>
, а затем построить Map<String, BucketOfAandB>
:
Map<String, List<Long>> idsBySignatureA = aList.stream()
.collect(Collectors.groupingBy(
MyInfoObject::getSignature,
Collectors.mapping(
MyInfoObject::getId,
Collectors.toList())));
Map<String, List<Long>> idsBySignatureB = bList.stream()
.collect(Collectors.groupingBy(
MyInfoObject::getSignature,
Collectors.mapping(
MyInfoObject::getId,
Collectors.toList())));
Map<String, List<BucketOfAandB>> result = Stream.concat(idsBySignatureA.entrySet().stream(), idsBySignatureB.entrySet().stream())
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(entry ->
new BucketOfAandB(
idsBySignatureA.get(entry.getKey()),
idsBySignatureB.get(entry.getKey())),
Collectors.toList())
));
Также не стесняйтесь извлекать первую часть в функцию для лучшей читабельности.
Похожие вопросы
Новые вопросы
java
Java - это язык программирования высокого уровня. Используйте этот тег, если у вас возникли проблемы с использованием или пониманием самого языка. Этот тег редко используется отдельно и чаще всего используется вместе с [spring], [spring-boot], [jakarta-ee], [android], [javafx], [hadoop], [gradle] и [maven].