当前位置:  开发笔记 > 编程语言 > 正文

为什么迭代映射的排序流会比所需的元素多?

如何解决《为什么迭代映射的排序流会比所需的元素多?》经验,为你挑选了1个好方法。

考虑这个小程序,我们在其中获取一个流,对其进行排序,映射,然后对其进行迭代:

public class AlphabetOrdinals {

    private static final List ALPHABET = List.of('a', 'b', 'c', 'd', 'e', 'f');
    private static final int STOP_ORDINAL = 'b' - 'a';

    public static void main(String[] args) {
        System.out.println("java.runtime.version = " + System.getProperty("java.runtime.version"));

        Stream ordinals = ALPHABET.stream()
                                           .sorted()
                                           .map(AlphabetOrdinals::ordinal);

        int count = 0;

        Iterator iterator = ordinals.iterator();
        while (iterator.hasNext()) {
            int ordinal = iterator.next();
            if (ordinal > STOP_ORDINAL) {
                System.out.println("stopping at " + ordinal);
                break;
            }
            System.out.println("consuming " + ordinal);
            ++count;
        }

        System.out.println("consumed " + count + " ordinals");
    }

    private static int ordinal(char letter) {
        int ordinal = letter - 'a';
        System.out.println("performing EXTREMELY EXPENSIVE mapping of " + letter + " -> " + ordinal);
        return ordinal;
    }

}

这个程序很愚蠢,但是与实际程序简化了,在实际程序中,迭代与另一个流上的迭代交织在一起,所以我不能轻易地用takeWhile / forEach替换它。

我希望该程序可以打印:

java.runtime.version = 11+28
performing EXTREMELY EXPENSIVE mapping of a -> 0
consuming 0
performing EXTREMELY EXPENSIVE mapping of b -> 1
consuming 1
performing EXTREMELY EXPENSIVE mapping of c -> 2
stopping at 2
consumed 2 ordinals

但它打印:

java.runtime.version = 11+28
performing EXTREMELY EXPENSIVE mapping of a -> 0
performing EXTREMELY EXPENSIVE mapping of b -> 1
performing EXTREMELY EXPENSIVE mapping of c -> 2
performing EXTREMELY EXPENSIVE mapping of d -> 3
performing EXTREMELY EXPENSIVE mapping of e -> 4
performing EXTREMELY EXPENSIVE mapping of f -> 5
consuming 0
consuming 1
stopping at 2
consumed 2 ordinals

如果我删除.sorted(),它会打印出我期望的样子。

为什么会这样?

在实际程序中,映射步骤涉及从速度较慢的网络驱动器中读取数据负载,因此,我不希望这样做比绝对必要的次数更多!



1> 小智..:

无聊的答案:

这只是流API实现的编写方式。

少无聊的答案:

流具有某种操作链,可应用于输入。对于引用流,为排序而添加的操作是:(java.util.stream.SortedOps.RefSortingSink假设您拥有与我相似的JDK)。对于地图,它是:

new StatelessOp(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
            @Override
            Sink opWrapSink(int flags, Sink sink) {
                return new Sink.ChainedReference(sink) {
                    @Override
                    public void accept(P_OUT u) {
                        downstream.accept(mapper.apply(u));
                    }
                };
            }
        };

实施的相关部分在java.util.stream.SortedOps.RefSortingSink这里:

  @Override
        public void begin(long size) {
            if (size >= Nodes.MAX_ARRAY_SIZE)
                throw new IllegalArgumentException(Nodes.BAD_SIZE);
            list = (size >= 0) ? new ArrayList((int) size) : new ArrayList();
        }

        @Override
        public void end() {
            list.sort(comparator);
            downstream.begin(list.size());
            if (!cancellationWasRequested) {
                list.forEach(downstream::accept);
            }
            else {
                for (T t : list) {
                    if (downstream.cancellationRequested()) break;
                    downstream.accept(t);
                }
            }
            downstream.end();
            list = null;
        }

        @Override
        public void accept(T t) {
            list.add(t);
        }

如您所见,整个列表上的已排序传递传递到链中的下一个操作(下一个操作称为downstream)。但是,映射操作会接收其接收的任何内容,使用映射功能,然后将其传递给下游。这意味着,如果仅使用map,您将获得懒惰的预期行为,而如果使用sorted,则整个已排序的流都将被塞进map的喉咙中list.forEach(downstream::accept),而map不能拒绝接受它,或者只能接受它的一部分。


@JohannesKuhn _I_完全相反,如果您使用迭代器_are_,则绝对可以更早地终止它。但可能只是我
推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有