简体   繁体   中英

Convert logic to use Java 8 Streams

I am learning how to use Streams in Java 8, and would like to understand how to convert the following example to a stream-based way. I have made a couple attempts but can't get the stream-based way to compile. I think I'm getting tripped up with nested loops and variable references that seem to be lost.

import java.util.Collection;
import java.util.Objects;

import gov.geo.argcci.product.parts.layers.PlatformMarkerLayer;

public class Test {

    public void java7Method(final Collection<Item> items) {
        for (final LayerHolder layerHolder : getLayerHolders()) {
            if (layerHolder.getLayer() instanceof MyLayer) {
                final MyLayer myLayer = (MyLayer) layerHolder.getLayer();
                final Item current = myLayer.getItem();
                if (current != null) {
                    for (final Item item : items) {
                        if (Objects.equals(item.getSomeKey(), current.getSomeKey())
                                && Objects.equals(item.getSomeOtherKey(), current.getSomeOtherKey())) {
                            final SomeObject someObject = getObjectBasedOnItem(current);
                            someObject.doSomething(layerHolder);
                        }
                    }
                }
            }
        }
    }

    public void java8Method(final Collection<Item> items) {
        getLayerHolders()
        .stream()
        .filter(layerHolder -> layerHolder.getLayer() instanceof MyLayer)
                .map(layerHolder -> (MyLayer) layerHolder.getLayer())
                .map(layerHolder -> layerHolder.getItem())
                .filter(Objects::nonNull)
                .forEach(current->{
                    items.stream()
                    .filter(Objects.equals(item.getSomeKey(), current.getSomeKey()) && Objects.equals(item.getSomeOtherKey(), current.getSomeOtherKey()));              
//                      final SomeObject someObject = getObjectBasedOnItem(current);
//                      someObject.doSomething(layerHolder);
                });
    }

    // The Code below this line is auto-generated to make sure example can compile without errors
    private SomeObject getObjectBasedOnItem(Item current) {
        return null;
    }
    private Collection<LayerHolder> getLayerHolders() {
        return null;
    }
    private class Item {
        public Object getSomeKey() {
            return null;
        }
        public Object getSomeOtherKey() {
            return null;
        }
    }
    private class LayerHolder {
        public MyLayer getLayer() {
            return null;
        }
    }
    private class MyLayer {
        public Item getItem() {
            return null;
        }
    }
    private class SomeObject {
        public void doSomething(LayerHolder layerHolder) {}
    }
}

A direct translation of your Java 7 code would be

public void java8Method(final Collection<Item> items) {
    getLayerHolders().forEach(layerHolder -> {
        Optional.of(layerHolder)
                .map(LayerHolder::getLayer)
                .filter(MyLayer.class::isInstance)
                .map(l -> ((MyLayer)l).getItem())
                .ifPresent(current-> items.stream()
                    .filter(item ->
                        Objects.equals(item.getSomeKey(), current.getSomeKey())
                     && Objects.equals(item.getSomeOtherKey(), current.getSomeOtherKey()))
                    .forEach(ignored ->
                             getObjectBasedOnItem(current).doSomething(layerHolder))
            );
    });
}

except that this doesn't call getLayer() twice.

But I have the strong feeling that what you actually want to do is

public void java8Method(final Collection<Item> items) {
    getLayerHolders().forEach(layerHolder ->
        Optional.of(layerHolder)
                .map(LayerHolder::getLayer)
                .filter(MyLayer.class::isInstance)
                .map(l -> ((MyLayer)l).getItem())
                .filter(current-> items.stream()
                    .anyMatch(item ->
                        Objects.equals(item.getSomeKey(), current.getSomeKey())
                     && Objects.equals(item.getSomeOtherKey(), current.getSomeOtherKey())))
                .ifPresent(current ->
                           getObjectBasedOnItem(current).doSomething(layerHolder))
    );
}

Generally, you should think more about the actual program logic than converting pre-Java 8 code to Stream API using code…

You need to use the outer layer object in the doSomething method. So you can't map to the Item object in the stream.

Probably the best decision is to try to simplify the design. Using streams is not going to make it more simple to implement.

In your version without streams, the inner for loop is testing if the item of the layer is inside the list, this could be another filter in the stream.

The code could be:

public void java8Method(final Collection<Item> items) {
    getLayerHolders()
    .stream()
    .filter(layerHolder -> layerHolder.getLayer() instanceof MyLayer)
    .filter(layerHolder -> Objects.nonNull(((MyLayer) layerHolder.getLayer()).getItem()))
    .filter(layerHolder->{ Item current = ((MyLayer) layerHolder.getLayer()).getItem();
                           return items.stream()
                                   .anyMatch(item->Objects.equals(item.getSomeKey(), current.getSomeKey()) && 
                                                   Objects.equals(item.getSomeOtherKey(), current.getSomeOtherKey())); })
    .forEach(layerHolder-> getObjectBasedOnItem(((MyLayer) layerHolder.getLayer()).getItem()).doSomething(layerHolder));
}

Trying to make it a single streaming function makes it very difficult to read and maintain.

Implementing it in stages and breaking out Predicates would be ideal.

This implementation gets you both readable, maintainable code and streaming functionality.

public void java8Method(Collection<Item> items)
  {
    Predicate<LayerHolder> hasMyLayer = lh -> MyLayer.class
        .isInstance(lh.getLayer());

    Predicate<MyLayer> hasNonNullItem = ml -> Objects.nonNull(ml.getItem());

    Predicate<MyLayer> hasMatchingItem = (ml) -> items.stream()
        .filter(i -> i.getSomeKey().equals(ml.getItem().getSomeKey()))
        .filter(i -> i.getSomeOtherKey().equals(ml.getItem().getSomeOtherKey()))
        .count() > 0;

    getLayerHolders().stream().filter(lh -> hasMyLayer.test(lh))
        .filter(lh -> hasNonNullItem.test(lh.getLayer()))
        .filter(lh -> hasMatchingItem.test(lh.getLayer()))
        .forEach(lh -> getObjectBasedOnItem(lh.getLayer().getItem())
            .doSomething(lh));
  }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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