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)

另请注意,它不添加地图步骤。相反,它在过滤后使用不安全的转换,因为它已经知道所有流元素现在都是狗。因此管道保持较短。