来自this的问题。 有这样的层次结构。 A是基类的地方:
A
/ \
B C
| A | | B | | C |
| getId()| |A.getId() | |A.getId()|
|isVisible()|
以及以下内容:
List<A> mappings;
我想将 B 实例的所有ID映射到B.isVisible()的值,将 C 的实例ID映射到 TRUE < /强>
在初始问题的帮助下,我将其改进为这种格式:
mappings.stream().filter(a -> a instanceof B)
.map(b -> (B)b)
.collect(Collectors.toMap(A::getId, m -> m.isVisible()));
丑陋的版本是:
mappings.stream()
.collect(Collectors.toMap(A::getId, m ->
{
boolean isB = m instanceof B;
return isB ? ((B) m).isVisible() : true;
}));
有关改进它的任何帮助,以便为更优雅的版本提供默认值?
答案 0 :(得分:7)
你的变种
mappings.stream()
.collect(Collectors.toMap(A::getId, m ->
{
boolean isB = m instanceof B;
return isB ? ((B) m).isVisible() : true;
}));
并不是那么难看,因为它表达了你的意图。
但是你可以简化它,因为你不需要一个局部变量来保存m instanceof B
:
mappings.stream()
.collect(Collectors.toMap(A::getId, m->m instanceof B? ((B)m).isVisible(): true));
然后,根据经验,只要复合布尔表达式中有boolean
个文字,就会有一个没有它的替代品。
mappings.stream()
.collect(Collectors.toMap(A::getId, m -> !(m instanceof B) || ((B)m).isVisible()));
答案 1 :(得分:3)
也许你可以通过助手类做你想做的事情:
class Helper {
private final Long id;
private final boolean visible;
Helper(A a) {
this.id = a.getID();
this.visible = a instanceof B ? ((B) a).isVisible() : true;
}
Long getId() { return id; }
boolean isVisible() { return visible; }
}
然后,将列表的每个元素映射到Helper
的实例并收集到地图:
Map<Long, Boolean> map = mappings.stream()
.map(Helper::new)
.collect(Collectors.toMap(Helper::getId, Helper::isVisible));
此解决方案只是将visible
true
false
或Helper
委托给Boolean
类,并让您拥有一个干净的流管道。
作为附注...通常,使用类型为Set
的值的地图是没有意义的,因为您可以使用Set<Long> set = mappings.stream()
.map(Helper::new)
.filter(Helper::isVisible)
.collect(Collectors.toSet());
具有相同的语义:
boolean isVisible = set.contains(elementId);
然后,要知道某个元素是否可见,只需检查它是否属于集合:
$destinations = get_posts( array(
'post_type' => 'destination_showcase',
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'destination_state',
'value' => ':"'.$state_id . '";' , // looking for serialized value in quotes
'compare' => 'LIKE'
)
),
'orderby' => 'title',
'order' => 'ASC',
) );
答案 2 :(得分:3)
如果您无法更改源代码,可以编写实用程序方法isA
来描述您想要的内容,例如:
Map<Integer, Boolean> visibility = mappings.stream().collect(toMap(
A::getId,
isA(B.class, B::isVisible, any -> true)
));
static <T, S extends T, R> Function<T, R> isA(Class<? extends S> type,
Function<? super S, R> special,
Function<T, R> general) {
return it -> type.isInstance(it) ? special.apply(type.cast(it))
: general.apply(it);
}
答案 3 :(得分:2)
您的代码很难看,因为您的层次结构没有意义。你可能想要的是:
class A
{
abstract public boolean isVisible();
// or make it concrete and return a default if you need to
}
// B can stay the same (+ @Override)
class C extends A
{
@Override
public boolean isVisible()
{
return true;
}
}
然后就可以了:
mappings.stream()
.collect(Collectors.toMap(A::getId, m -> m.isVisible()));
答案 4 :(得分:1)
可能在流中将C&C映射为null,然后在null上返回true?像这样:
mappings.stream().map(a -> a instanceof C ? (B)null : (B)a)
.collect(Collectors.toMap(A::getId, m==null || m.isVisible()));
答案 5 :(得分:0)
我已经编写了这个简单的Collector
实现,它可以做你想做的事情:
public class AToMapCollector implements Collector<A, Map<Integer, Boolean>, Map<Integer, Boolean>>{
@Override
public Supplier<Map<Integer, Boolean>> supplier(){
return HashMap::new;
}
@Override
public BiConsumer<Map<Integer, Boolean>, A> accumulator(){
return (map, a) -> {
boolean visible = true;
if(a instanceof B){
visible = ((B) a).isVisible();
}
map.put(a.getId(), visible);
};
}
@Override
public BinaryOperator<Map<Integer, Boolean>> combiner(){
return (map1, map2) -> {
map1.putAll(map2);
return map1;
};
}
@Override
public Function<Map<Integer, Boolean>, Map<Integer, Boolean>> finisher(){
return map -> map;
}
@Override
public Set<Characteristics> characteristics(){
return EnumSet.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED);
}
}
然后最终可以这样调用:
Map<Integer, Boolean> map = mappings.stream().collect(new AToMapCollector());
创建一个全新类的增加是这个收集器的可重用性,并且增加了可读性而不是多行lambda。