[英]Java Stream.map() on Subclasses with Wildcards Not Working
我有兩個類ChildA
和ChildB
,它們是Parent
的子類。
我得到了一個Parent
列表,並希望使用 Stream 將它們映射到Map<? extends Parent, List<? extends Parent>>
Map<? extends Parent, List<? extends Parent>>
問題是.map
部分似乎正在刪除子類。 這是一些說明我的問題的示例代碼:
items.stream()
.map(item -> item.isA() ? item.getA() : item.getB()) //<--- issue here, returns List<Parent> instead of List<? extends Parent>
.collect(Collectors.groupingBy(i -> i.getClass()));
有了上面,輸出是: Map<? extends Class, List<Parent>>
Map<? extends Class, List<Parent>>
但我想要Map<?extends Class, List<? extends Parent>>
Map<?extends Class, List<? extends Parent>>
所以我可以做類似的事情:
map.get(A.class)
-> List<A>
所以我可以很容易地分配兩個變量:
List<A> a = map.get(a.class);
List<b> b = map.get(b.class);
現在我必須做類似的事情: map.get(A.class).stream().map(A::cast).collect(Collectors.toList());
有沒有辦法得到我想要的結果?
編輯:
這是一個最小的可重現示例: https ://replit.com/@rgurn/MinExample?v=1#Main.java
請注意,創建容器類Item
不是我的設計選擇,我無法更改它。
好的,這就是我想出的。 按照以前的方式進行操作,但您需要通過強制轉換來重建列表。
List<Item> items = List.of(new Item("a", new A()),
new Item("b", new B()));
Map<Class<?>, List<Parent>> map = items
.stream()
.map(item -> item.isA() ? item.getA() : item.getB())
.collect(Collectors.groupingBy(p->p.getClass()));
List<A> list = new ArrayList<>();
for (Parent p :map.get(A.class)) {
list.add(A.class.cast(p));
}
這不能很好地擴展,但您始終可以使用映射鍵投射列表的內容。 你不應該得到任何類轉換異常,因為你是根據類在地圖中分離它們。
在悲傷地意識到我對List
有誤解並且無法做我最初嘗試的事情之后,我最終采用了像 @M 這樣的基於Pair
的方法。 普羅霍羅夫建議:
Pair<ArrayList<A>, ArrayList<B>> collection = items.stream()
.map(i -> i.isA() ? i.getA() : i.getB())
.collect(() -> Pair.of(new ArrayList<A>(), new ArrayList<B>()), (p, u) -> {
if (u instanceof A) {
p.getFirst().add((A) u);
} else {
p.getSecond().add((B) u);
}
}, (p1, p2) -> {
p1.getFirst().addAll(p2.getFirst());
p1.getSecond().addAll(p2.getSecond());
});
List<A> aList = collection.getFirst();
List<B> bList = collection.getSecond();
總的來說,我對此不太滿意。 感覺有些不對勁,也許我應該親吻一下。 如果有人有更好的方法來做到這一點,我會很高興看到它。
盡管我仍然認為您不需要它,但這是 Item.class 的干凈實現:
public class Item {
private final Parent item;
public Item(Parent item) {
this.item = item;
}
public Parent get() {
return item;
}
public boolean isA() {
return item instanceof A;
}
public boolean isB() {
return item instanceof B;
}
public A getA() {
return (A) item;
}
public B getB() {
return (B) item;
}
public String getType() {
return getClass().getSimpleName();
}
}
您的.map
調用除了將Item
映射到Parent
之外什么都不做(無論 item 是否為 A,它只是將屬性item
添加回)。
但是您無論如何都無法將您的Stream
收集為所需的Map
( java 中沒有這樣的Map
),所以我為您制作了一個:
public class ClassMap<V> {
private final HashMap<Class<? extends V>, List<? extends V>> map;
public ClassMap() {
this.map = new HashMap<>();
}
public ClassMap(@NonNull List<V> input) {
this();
for (V v : input) add(v);
}
public <W extends V> void add(@NonNull W item) {
Class<W> clazz = (Class<W>) item.getClass();
List<W> list = (List<W>) map.get(clazz);
if (list == null) map.put(clazz, list = new ArrayList<>());
list.add(item);
}
public <W extends V> List<W> get(@NonNull Class<W> clazz) {
return (List<W>) map.get(clazz);
}
public int size() {
return map.size();
}
}
您可以像這樣創建一個: ClassMap<Parent> map = new ClassMap<>(items.stream().map(Item::get).collect(Collectors.toList()));
.
從 Java 12 開始,您可以使用teeing 收集器並通過使用標准收集器鏈而不是您自己的基於函數的自定義收集器來收集成對:
import static java.util.stream.Collectors.filtering;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
Pair<List<A>, List<B>> lists = items.stream().collect(
Collectors.teeing(
filtering(Parent::isA, mapping(Parent::getA, toList())),
filtering(Parent::isB, mapping(Parent::getB, toList())),
Pair::of
)
);
List<A> as = lists.getFirst();
List<B> bs = lists.getSecond();
當然,這假設Parent::isA
::isA 、 Parent::isB
和其他方法存在,但是可以很容易地以靜態實用程序函數的形式滾動添加。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.