简体   繁体   English

如何在 Java 中使 stream 管道更具可变性?

[英]How could I make a stream pipeline more variable in Java?

I wrote a stream pipeline:我写了一个 stream 管道:

private void calcMin(Clazz clazz) {
    OptionalInt min = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .min();
    list.add(min.getAsInt());
}

This pipeline gives me the lowest UserNumber .这个管道给了我最低的UserNumber

So far, so good.到目前为止,一切都很好。

But I also need the greatest UserNumber .但我也需要最大的UserNumber And I also need the lowest GroupNumber .而且我还需要最低的GroupNumber And also the greatest GroupNumber .也是最大的GroupNumber

I could write:我可以写:

private void calcMax(Clazz clazz) {
    OptionalInt max = listOfObjects.stream().filter(y -> (y.getName()
                    .matches(clazz.getFilter())))
                    .map(y -> (y.getUserNumber()))
                    .mapToInt(Integer::intValue)
                    .max();
    list.add(max.getAsInt());
}

And I could also write the same for .map(y -> (y.getGroupNumber())) .我也可以为.map(y -> (y.getGroupNumber()))写同样的内容。

This will work, but it is very redudant.这将起作用,但它非常多余。

Is there a way to do it more variable?有没有办法让它变得更加可变?

There are two differences in the examples: the map() operation, and the terminal operation ( min() and max() ).示例中有两个不同之处: map()操作和终端操作( min()max() )。 So, to reuse the rest of the pipeline, you'll want to parameterize these.因此,要重用管道的 rest,您需要参数化这些。

I will warn you up front, however, that if you call this parameterized method directly from many places, your code will be harder to read.但是,我会提前警告您,如果您直接从许多地方调用此参数化方法,您的代码将更难阅读。 Comprehension of the caller's code will be easier if you keep a helper function—with a meaningful name—that delegates to the generic method.如果您保留一个帮助函数(具有一个有意义的名称)来委托给泛型方法,那么对调用者的代码的理解会更容易。 Obviously, there is a balance here.显然,这里有一个平衡。 If you wanted to add additional functional parameters, the number of helper methods would grow rapidly and become cumbersome.如果你想添加额外的函数参数,辅助方法的数量会迅速增加并且变得很麻烦。 And if you only call each helper from one place, maybe using the underlying function directly won't add too much clutter.如果你只从一个地方调用每个助手,也许直接使用底层的 function 不会增加太多的混乱。

You don't show the type of elements in the stream.您没有显示 stream 中的元素类型。 I'm using the name MyClass in this example as a placeholder.在此示例中,我使用名称MyClass作为占位符。

    private static OptionalInt extremum(
        Collection<? extends MyClass> input,
        Clazz clazz,
        ToIntFunction<? super MyClass> valExtractor,
        Function<IntStream, OptionalInt> terminalOp) {

        IntStream matches = input.stream()
            .filter(y -> y.getName().matches(clazz.getFilter()))
            .mapToInt(valExtractor);
        return terminalOp.apply(matches);
    }

    private OptionalInt calcMinUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::min);
    }

    private OptionalInt calcMaxUserNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getUserNumber, IntStream::max);
    }

    private OptionalInt calcMinGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::min);
    }

    private OptionalInt calcMaxGroupNumber(Clazz clazz) {
        return extremum(listOfObjects, clazz, MyClass::getGroupNumber, IntStream::max);
    }

    ...

And here's a usage example:这是一个使用示例:

calcMaxGroupNumber(clazz).ifPresent(list::add);

The solution may reduce redundancy but it removes readability from the code.该解决方案可能会减少冗余,但会消除代码的可读性。

 IntStream maxi = listOfObjects.stream().filter(y -> (y.getName()
                .matches(clazz.getFilter())))
                .map(y -> (y.getUserNumber()))
                .mapToInt(Integer::intValue);
    System.out.println(applier(() -> maxi, IntStream::max));
    //System.out.println(applier(() -> maxi, IntStream::min));

... ...

public static OptionalInt applier(Supplier<IntStream> supplier, Function<IntStream, OptionalInt> predicate) {
    return predicate.apply(supplier.get());
}

For the sake of variety, I want to add the following approach which uses a nested Collectors.teeing (Java 12 or higher) which enables to get all values by just streaming over the collection only once.为了多样化,我想添加以下方法,它使用嵌套的Collectors.teeing (Java 12 或更高版本),它可以通过仅在集合上流式传输一次来获取所有值。

For the set up, I am using the below simple class:对于设置,我使用以下简单的 class:

@AllArgsConstructor
@ToString
@Getter
static class MyObject {
    int userNumber;
    int groupNumber;
}

and a list of MyObjects:和 MyObjects 列表:

List<MyObject> myObjectList = List.of(
        new MyObject(1, 2),
        new MyObject(2, 3),
        new MyObject(3, 4),
        new MyObject(5, 3),
        new MyObject(6, 2),
        new MyObject(7, 6),
        new MyObject(1, 12));

If the task was to get the max and min userNumber one could do a simple teeing like below and add for example the values to map:如果任务是获取最大和最小用户编号,则可以进行如下简单的teeing ,并将例如值添加到 map:

Map<String , Integer> maxMinUserNum =
        myObjectList.stream()
                    .collect(
                            Collectors.teeing(
                                    Collectors.reducing(Integer.MAX_VALUE, MyObject::getUserNumber, Integer::min),
                                    Collectors.reducing(Integer.MIN_VALUE, MyObject::getUserNumber, Integer::max),
                                    (min,max) -> {
                                        Map<String,Integer> map = new HashMap<>();
                                        map.put("minUser",min);
                                        map.put("maxUser",max);
                                        return map;
                                    }));
System.out.println(maxMinUserNum);

//output: {minUser=1, maxUser=7}

Since the task also includes to get the max and min group numbers, we could use the same approach as above and only need to nest the teeing collector:由于任务还包括获取最大和最小组数,我们可以使用与上述相同的方法,只需要嵌套 teeing 收集器:

Map<String , Integer> result =
    myObjectList.stream()
                .collect(
                        Collectors.teeing(
                                Collectors.teeing(
                                        Collectors.reducing(Integer.MAX_VALUE, MyObject::getUserNumber, Integer::min),
                                        Collectors.reducing(Integer.MIN_VALUE, MyObject::getUserNumber, Integer::max),
                                        (min,max) -> {
                                            Map<String,Integer> map = new LinkedHashMap<>();
                                            map.put("minUser",min);
                                            map.put("maxUser",max);
                                            return map;
                                        }),
                                Collectors.teeing(
                                        Collectors.reducing(Integer.MAX_VALUE, MyObject::getGroupNumber, Integer::min),
                                        Collectors.reducing(Integer.MIN_VALUE, MyObject::getGroupNumber, Integer::max),
                                        (min,max) -> {
                                            Map<String,Integer> map = new LinkedHashMap<>();
                                            map.put("minGroup",min);
                                            map.put("maxGroup",max);
                                            return map;
                                        }),
                                (map1,map2) -> {
                                    map1.putAll(map2);
                                    return map1;
                                }));

System.out.println(result);

output output

{minUser=1, maxUser=7, minGroup=2, maxGroup=12}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM