Java 8 - 使用外部参数过滤集合

时间:2015-04-27 08:17:36

标签: casting java-8 java-stream

我有一个包含具体后继者的基类(动物)地图 我想使用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)

或类似的东西。

4 个答案:

答案 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)

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