将链接的对象转换为流或集合

2022-09-02 11:35:24

我想迭代堆栈跟踪。堆栈跟踪由可抛出对象组成,其 getCause() 返回下一个可抛出对象。最后一次调用 getCause() 返回 null。(示例:a -> b -> null)

我尝试使用Stream.iterable()来生成NullPointerException,因为可迭代中的元素不能为null。以下是该问题的简短演示:

  public void process() {
      Throwable b = new Throwable();
      Throwable a = new Throwable(b);
      Stream.iterate(a, Throwable::getCause).forEach(System.out::println);
  }

我目前正在使用 while 循环手动创建集合:

public void process() {
    Throwable b = new Throwable();
    Throwable a = new Throwable(b);

    List<Throwable> list = new ArrayList<>();
    Throwable element = a;
    while (Objects.nonNull(element)) {
      list.add(element);
      element = element.getCause();
    }
    list.stream().forEach(System.out::println);
  }

有没有更好的方法(更短,更实用)来实现这一目标?


答案 1

问题是 中缺少停止条件。在Java 9中,您可以使用Stream.iterate

Stream.iterate(exception, Objects::nonNull, Throwable::getCause)

这相当于Java 9的

Stream.iterate(exception, Throwable::getCause)
      .takeWhile(Objects::nonNull)

请参阅 Stream.iterateStream.takeWhile

由于 Java 8 中不存在此功能,因此需要一个反向端口:

public static <T> Stream<T>
                  iterate​(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
{
    Objects.requireNonNull(next);
    Objects.requireNonNull(hasNext);
    return StreamSupport.stream(
        new Spliterators.AbstractSpliterator<T>(Long.MAX_VALUE, Spliterator.ORDERED) {
            T current = seed;
            int state;
            public boolean tryAdvance(Consumer<? super T> action) {
                Objects.requireNonNull(action);
                T value = current;
                if(state > 0) value = next.apply(value);
                else if(state == 0) state = 1;
                else return false;
                if(!hasNext.test(value)) {
                    state = -1;
                    current = null;
                    return false;
                }
                action.accept(current = value);
                return true;
            }
        },
        false);
}

语义与 Java 9 的语义相同:Stream.iterate

MyStreamFactory.iterate(exception, Objects::nonNull, Throwable::getCause)
               .forEach(System.out::println); // just an example

答案 2

我认为你可以在这里做一个递归调用:

static Stream<Throwable> process(Throwable t) {
    return t == null ? Stream.empty() : Stream.concat(Stream.of(t), process(t.getCause()));
}