我有一个包含具体后继者的基类(动物)地图
我想使用java流过滤并获取一个数组,每次我编写以下完整的长行代码:
(例如按具体的狗类过滤)
MyBaseObjectAnimalMap.values().stream().
filter(x -> x instanceof Dog).
map(x -> (Dog) x).
toArray(Dog[]::new);
有没有办法抽象出来?
我想用以下签名实现一个私有方法:
filterMapByType(Map<String,Animal> map, Class<T extends Animal> type)
或类似的东西。
答案 0 :(得分:8)
您可以提供IntFunction
来调用toArray
,以便获得T类型的数组而不是对象数组。
public static <T extends Animal> T[] filterMapByType(Map<String, Animal> map, Class<T> type, IntFunction<T[]> generator) {
return map.values()
.stream()
.filter(type::isInstance)
.map(type::cast)
.toArray(generator);
}
以及电话的一个例子:
Dog[] dogs = filterMapByType(map, Dog.class, Dog[]::new);
Dog[]::new
等同于length -> new Dog[length]
,即一个将int作为参数并返回大小为Dog
的类型length
的数组的函数。 / p>
如果可以返回列表,则可以使用.collect(toList());
代替.toArray(generator);
。
答案 1 :(得分:3)
当然可以。您唯一要做的就是使用Class#isAssignableFrom(Class)方法而不是instanceof
。哦,并使用Reflection创建该阵列当然。请查看Array.html#newInstance(Class, int)。
因此,最终结果看起来像这样(虽然未经测试):
filterMapByType(Map<String, Animal> map, Class<T extends Animal> type) {
return map.values().stream().
filter(animal -> type.isAssignableFrom(animal.getClass())).
map(type::cast).
toArray(Array.newInstance(type, map.size()));
}
答案 2 :(得分:2)
是的,您可以确定外部化谓词条件,并且在运行时根据谓词条件,它可以决定要过滤哪一个。
例如我已经编写了一个示例程序来演示它。
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class AnimalSelector {
public static void main(String args[]){
List<Animal> animalList = Arrays.asList(new Dog(),new Horse(),new Human(),new Dog(),new Horse(),new Human());
Predicate<Animal> dogFilter = x-> x instanceof Dog;
System.out.println(toAnimalArray(animalList,dogFilter));
Predicate<Animal> humanFilter = x-> x instanceof Human;
System.out.println(toAnimalArray(animalList,humanFilter));
Predicate<Animal> horseFilter = x-> x instanceof Dog;
System.out.println(toAnimalArray(animalList,horseFilter));
}
public static <Animal> List<Animal> toAnimalArray(List<Animal> animalList,Predicate<Animal> filterCondition){
return animalList.stream()
.filter(filterCondition)
.collect(Collectors.toList());
}
}
interface Animal{
public void eat();
}
class Dog implements Animal{
private String animalType;
public Dog( ) {
this.animalType = "dog";
}
@Override
public void eat() {
}
@Override
public String toString() {
return "Dog{" +
"animalType=" + animalType +
'}';
}
}
class Human implements Animal{
private String animalType;
public Human( ) {
this.animalType = "Human";
}
@Override
public void eat() {
}
@Override
public String toString() {
return "Human{" +
"animalType=" + animalType +
'}';
}
}
class Horse implements Animal{
private String animalType;
public Horse( ) {
this.animalType = "Horse";
}
@Override
public void eat() {
}
@Override
public String toString() {
return "Horse{" +
"animalType=" + animalType +
'}';
}
}
答案 3 :(得分:0)
最近我写了一个名为StreamEx的小型图书馆,其中包括很容易解决您的问题:
StreamEx.ofValues(MyBaseObjectAnimalMap).select(Dog.class).toArray(Dog[]::new)
另请注意,它不会添加地图步骤。相反,它在过滤后使用不安全的强制转换,因为已经知道所有流元素现在都是狗。因此管道仍然较短。