Java 8 - 使用外部参数过滤集合
Java 8 - filtering a collection using an external parameter
我有一张包含具体后继者的基地 class(动物)地图。
我想使用 java 流过滤并获取一个数组,每次我编写以下完整的长代码行时:
(例如通过具体的 Dog class 过滤)
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)
或类似的东西。
当然是。您唯一需要做的不同就是为此使用 Class#isAssignableFrom(Class) method rather than instanceof
. Oh, and use Reflection to create that Array of course. Check out 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()));
}
您可以提供一个 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,returns一个Dog
类型的数组,大小为length
.
如果可以返回列表,那么您可以使用 .collect(toList());
而不是 .toArray(generator);
。
是的,您当然可以将谓词条件外部化,并且在 运行 时,它可以根据谓词条件决定过滤哪一个条件。
例如我写了一个示例程序来演示它。
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 +
'}';
}
}
最近我写了一个叫做 StreamEx 的小库,它可以很容易地解决你的问题:
StreamEx.ofValues(MyBaseObjectAnimalMap).select(Dog.class).toArray(Dog[]::new)
另请注意,它不添加地图步骤。相反,它在过滤后使用不安全的转换,因为它已经知道所有流元素现在都是狗。因此管道保持较短。
我有一张包含具体后继者的基地 class(动物)地图。
我想使用 java 流过滤并获取一个数组,每次我编写以下完整的长代码行时:
(例如通过具体的 Dog class 过滤)
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)
或类似的东西。
当然是。您唯一需要做的不同就是为此使用 Class#isAssignableFrom(Class) method rather than instanceof
. Oh, and use Reflection to create that Array of course. Check out 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()));
}
您可以提供一个 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,returns一个Dog
类型的数组,大小为length
.
如果可以返回列表,那么您可以使用 .collect(toList());
而不是 .toArray(generator);
。
是的,您当然可以将谓词条件外部化,并且在 运行 时,它可以根据谓词条件决定过滤哪一个条件。
例如我写了一个示例程序来演示它。
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 +
'}';
}
}
最近我写了一个叫做 StreamEx 的小库,它可以很容易地解决你的问题:
StreamEx.ofValues(MyBaseObjectAnimalMap).select(Dog.class).toArray(Dog[]::new)
另请注意,它不添加地图步骤。相反,它在过滤后使用不安全的转换,因为它已经知道所有流元素现在都是狗。因此管道保持较短。