Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turn linked Objects into Stream or Collection

I want to iterate over a stacktrace. The stacktrace consists of throwables whose getCause() returns the next throwable. The last call to getCause() returns null. (Example: a -> b -> null)

I've tried to use Stream.iterable() which results in a NullPointerException, since the elements in the iterable can't be null. Here is a short demonstration of the problem:

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

I'm currently using a while loop to create a collection manually:

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);
  }

Is there a better way (shorter, more functional) to achieve this?

like image 648
Arigion Avatar asked Sep 06 '17 12:09

Arigion


People also ask

Which method will be used to convert the collection objects into Stream object in java8?

stream(). mapToInt(AClass::getValue).

What is Stream () in Java?

A stream is a sequence of objects that supports various methods which can be pipelined to produce the desired result. The features of Java stream are – A stream is not a data structure instead it takes input from the Collections, Arrays or I/O channels.


2 Answers

The problem is the missing stop condition in Stream.iterate. In Java 9, you could use

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

which is equivalent to Java 9’s

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

See Stream.iterate or Stream.takeWhile.

Since this feature does not exist in Java 8, a back-port would be required:

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);
}

The semantic is the same as with Java 9’s Stream.iterate:

MyStreamFactory.iterate(exception, Objects::nonNull, Throwable::getCause)
               .forEach(System.out::println); // just an example
like image 199
Holger Avatar answered Oct 23 '22 07:10

Holger


I think that you can do a recursive call here:

static Stream<Throwable> process(Throwable t) {
    return t == null ? Stream.empty() : Stream.concat(Stream.of(t), process(t.getCause()));
}
like image 22
Eugene Avatar answered Oct 23 '22 07:10

Eugene