Search code examples
javajava-8java-streamreducechain

Java reduce list to fallback chain


Suppose I have a List of some entities in Java like

List<Entity> entities = Arrays.asList(entity1, entity2, entity3...);

I would like to reduce it into one instance of a chain object like:

class EntityChain {
    private final Entity entity;
    private final Optional<EntityChain> fallback;

    private EntityChain(Builder builder) {
        this.entity = builder.entity;
        this.fallback = builder.fallback;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static final class Builder {
        private Entity entity;
        private Optional<EntityChain> fallback = Optional.empty();           

        public Builder withEntity(Entity entity) {
            this.entity = entity;
            return this;
        }

        public Builder withFallback(EntityChain fallback) {
            this.fallback = Optional.of(fallback);
            return this;
        }

        public EntityChain build() {
            return new EntityChain(this);
        }
    }
}

EntityChain is immutable and has a builder.
So that the result would be an EntityChain instance like:

chain
   -> entity = entity1
   -> fallback
        -> entity = entity2
        -> fallback
            -> entity = entity3
            -> fallback
                ...

Is it possible to do this with some magic Java 8 fluent reduction?
Is

Stream.reduce(U identity,
              BiFunction<U, ? super T, U> accumulator,
              BinaryOperator<U> combiner)

applicable here? Using somehow it's builder?


Solution

  • after thinking, I found I can remove the holder Supplier<EntityChain> completely when reducing in sequentially stream. the algorithm is build the entity chain reversed: first building entity(n) , then entity(n-1), ... entity(0).

    BiFunction<EntityChain, Entity, EntityChain> reducing =
        (next, entity) -> Optional.ofNullable(next)
                        // create a builder with fallback if EntityChain present
                        .map(fallback -> EntityChain.builder().withFallback(fallback))
                        // create a builder without fallback
                        .orElseGet(EntityChain::builder)
                        //build the EntityChain
                        .withEntity(entity).build();
    
    // combiner never be used in sequentially stream
    BinaryOperator<EntityChain> rejectedInParallelStream = (t1, t2) -> {
        //when you use parallel the chain order maybe changed, and the result is wrong.
        throw new IllegalStateException("Can't be used in parallel stream!");
    };
    
    
    EntityChain chain = reverse(entities).
            stream().reduce(null, reducing, rejectedInParallelStream);
    
    
    //copy & reverse the copied List
    static <T> List<T> reverse(List<T> list) {
        List<T> it = list.stream().collect(Collectors.toList());
        Collections.reverse(it);
        return it;
    }
    

    Output

    -> entity = entity1
    -> fallback
        -> entity = entity2
        -> fallback (empty)