提问者:小点点

使用优先级流式传输集合和筛选器


我正在尝试根据某个优先级筛选收藏。集合由具有特定类型字段的实体组成。

因此,我想执行以下操作:通过集合进行迭代/流式传输,然后

> < li>

查找具有getType = "type1 "的第一个实体。如果这个实体有价格

找到下一个具有 getType = “type2” 的实体。如果这是有代价的

找到下一个具有 getType = “type3” 的实体。如果这是有代价的

找到下一个具有getType="type4"的实体。如果这有代价

找到下一个具有 getType = “type5” 的实体。如果这是有代价的

否则返回。

我正在尝试使用流来执行此操作,但过滤步骤是我无法创建此优先级过滤类型的地方。我还尝试使用for循环遍历集合,但不确定如何实现type1的findFirst实体,然后是type2等。


共3个答案

匿名用户

如果您稍微重新表述需求,它会使对流的一系列操作的转换更加清晰——您应该返回带有价格的第一项

MyEntity result = myList.stream()
                        .filter(e -> e.getPrice() > 0)
                        .min(Compartor.comparing(MyEntity::getType))
                        .orElse(null);

匿名用户

原始 - 无效还原方法

一种选择是颠倒操作顺序。例如:过滤其中'价格

stream.filter(elem -> elem.price > 0)
    .reduce((elem1, elem2) -> elem1.type.compareTo(elem2.type) < 0 ? elem1 : elem2)

编辑-更正确的还原方法

一般来说,最好保持流操作无状态。因此,创建一个处理遍历列表并返回结果的函数可能是更正确的方法。使用流,可以定义一个自定义的“减速器”,它可以跟踪检查的前一个类型,以确定下一个结果是否是可能的有效匹配。一旦找到有效匹配,它总是被返回。

    public static void main(String[] args)
    {
        List<Entity> data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
        System.out.println(data.stream().reduce(new Reducer()).filter(entity -> entity != Reducer.NO_MATCH));
    }

    /*Once a match is found, always use it. For a given type, only the first found entity of that type will be used*/
    public static final class Reducer implements BinaryOperator<Entity>
    {
        private int priorValidType;
        Reducer(){ this.priorValidType = 0; }

        @Override
        public Entity apply(Entity result, Entity newElem)
        {
            int nextValidType = priorValidType + 1;
            if(priorValidType > 0 && result != NO_MATCH) return result; /*Match already found, use it*/
            if(result.type == nextValidType && result.price > 0) { priorValidType = nextValidType; return result; } /*result is a match*/
            if(newElem.type == nextValidType && newElem.price > 0) { priorValidType = nextValidType; return newElem; } /*newElem is a match*/
            if(result.type == nextValidType || newElem.type == nextValidType) { priorValidType = nextValidType; }
            return NO_MATCH; /*No match has been found*/
        }

        public static final Entity NO_MATCH = new Entity(-1, -1);
    }

    public static final class Entity
    {
        private final int price, type;
        Entity(int type, int price){ this.price = price; this.type = type; }
        public String toString(){ return "(" + type + ", " + price + ")"; }
        int getPrice(){ return price; }
        int getType(){ return type; }
        public static Entity eee(int type, int price){ return new Entity(type, price); }
    }

编辑-使用过滤器的替代方法

可以创建一个过滤器,它执行类似于duce方法的操作,并且在调用“findFirst”时具有短路的好处。在下面,第一个过滤器只允许第一次遇到给定类型(按顺序)通过。第二个过滤器确认它是有效的。

    public static void main(String[] args)
    {
        List<Entity> data = Arrays.asList(eee(2, 6), eee(1, 0), eee(1, 10), eee(3, 7), eee(2, 0), eee(3, 5), eee(4, 0), eee(5, 0));
        System.out.println(data.stream().filter(new FirstTypeMatch()).filter(entity -> entity.price > 0).findFirst());
    }

    /*Filter where the element is the first of the given type*/
    public static final class FirstTypeMatch implements Predicate<Entity>
    {
        private int priorValidType = 0;
        @Override
        public boolean test(Entity nextElem)
        {
            if(nextElem.type == (priorValidType + 1)){ priorValidType++; return true; }
            return false;
        }
    }

匿名用户

根据Andreas的澄清和对目前所述要求的严格解释,这应该行得通。

  • 我基本上是对类型进行流式传输,然后对list进行子流式传输
  • 按照优先级顺序检查每种类型的内部流
  • 如果找到的第一个具有正价格,则返回该价格
  • 否则将检查下一个类型并重复该过程
  • 如果没有匹配项,则返回一个特殊对象

用于验证的类。

class MyClass {
    private int price;
    private String type;
    
    public MyClass(int price, String type) {
        this.price = price;
        this.type = type;
    }
    
    public int getPrice() {
        return price;
    }
    
    public String getType() {
        return type;
    }
    
    public String toString() {
        return String.format("[%s, %s]", price, type);
    }
}

数据

List<MyClass> list = List.of(new MyClass(0, "Type1"),
        new MyClass(10, "Type2"), new MyClass(2, "Type1"),
        new MyClass(2, "Type4"), new MyClass(2, "Type5"),
        new MyClass(2, "Type2"), new MyClass(10, "Type1"),
        new MyClass(1, "Type2"), new MyClass(2, "Type5"));

String[] types =
        { "Type1", "Type2", "Type3", "Type4", "Type5" };

MyClass selected = Arrays.stream(types)
        .map(type -> list.stream()
                .filter(obj -> obj.getType().equals(type))
                .findFirst()
                .orElse(null))
        .filter(a -> a != null && a.getPrice() > 0)
        .findFirst().orElse(new MyClass(-1, "Empty"));

System.out.println(selected);

版画

[10, Type2]