首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >do-while with Java8-可选

do-while with Java8-可选
EN

Stack Overflow用户
提问于 2015-02-26 19:48:42
回答 5查看 22K关注 0票数 10

在我的一些项目中,我经常使用do-while-checkNextForNull-getNext循环模式(不知道它是否有正式名称)。但在Java8中,使用Optional被认为是比在客户端代码中检查空引用更干净的代码。但是当在这个循环模式中使用Optional时,代码会变得有点冗长和丑陋,但是因为Optional有一些方便的methodS,所以我希望一定存在一种比我在下面提出的方法更简洁的方法。

示例:

给出下面的类。

代码语言:javascript
复制
class Item {
    int nr;

    Item(nr) {
        this.nr = nr;
        // an expensive operation
    }

    Item next() {
        return ...someCondition....
            ? new Item(nr + 1)
            : null;
    }
}

其中第一个项目始终具有nr==1,每个项目确定下一个项目,并且您不希望创建不必要的新项目。

我可以在客户端代码中使用以下循环do-while-checkNextForNull-getNext模式:

代码语言:javascript
复制
Item item = new Item(1);
do {
    // do something with the item ....
} while ((item = item.next()) != null);

使用Java8-Optional,给定的类变成:

代码语言:javascript
复制
class Item {
    ....

    Optional<Item> next() {
        return ...someCondition....
            ? Optional.of(new Item(nr + 1))
            : Optional.empty();
    }
}

然后do-while-checkNextForNull-getNext循环模式变得有点丑陋和冗长:

代码语言:javascript
复制
Item item = new Item(1);
do {
    // do something with the item ....
} while ((item = item.next().orElse(null)) != null);

orElse(null)) != null部分让人感觉不舒服。

我已经寻找了其他类型的循环,但还没有找到更好的。有没有更干净的解决方案?

更新:

可以使用for-each循环,同时避免空引用(使用空引用被认为是一种糟糕的做法)。这个解决方案是由Xavier Delamotte提出的,不需要Java8-Optional。

使用泛型迭代器实现:

代码语言:javascript
复制
public class Item implements Iterable<Item>, Iterator<Item> {
    int nr;

    Item(int nr) { 
        this.nr = nr;
        // an expensive operation
    }

    public Item next() {
        return new Item(nr + 1);
    }

    public boolean hasNext() {
        return ....someCondition.....;
    }

    @Override
    public Iterator<Item> iterator() {
        return new CustomIterator(this);
    }
}

代码语言:javascript
复制
class CustomIterator<T extends Iterator<T>> implements Iterator<T> {
    T currentItem;
    boolean nextCalled;

    public CustomIterator(T firstItem) {
        this.currentItem = firstItem;
    }

    @Override
    public boolean hasNext() {
        return currentItem.hasNext();
    }

    @Override
    public T next() {
        if (! nextCalled) {
            nextCalled = true;
            return currentItem;
        } else {
            currentItem = currentItem.next();
            return currentItem;
        }
    }
}

然后客户端代码就会变得非常简单/干净:

代码语言:javascript
复制
for (Item item : new Item(1)) {
    // do something with the item ....
}

尽管这可能被视为违反迭代器约定,因为new Item(1)对象包含在循环中,而通常情况下,for循环会立即调用next(),从而跳过第一个对象。换句话说:对于第一个对象,违反了returnS (),因为它本身就违反了第一个对象。

EN

回答 5

Stack Overflow用户

发布于 2015-02-26 19:54:42

你可以这样做:

代码语言:javascript
复制
Optional<Item> item = Optional.of(new Item(1));
do {
    Item value = item.get();
    // do something with the value ....
} while ((item = value.next()).isPresent());

或者(为了避免额外的变量):

代码语言:javascript
复制
Optional<Item> item = Optional.of(new Item(1));
do {
    // do something with item.get() ....
} while ((item = item.get().next()).isPresent());
票数 11
EN

Stack Overflow用户

发布于 2015-02-26 19:55:40

在Java8中,与检查客户端代码中的空引用相比,使用Optional被认为是更干净的代码

不,这是另一种方式: Optional可以用来帮助编写更整洁的代码。如果不是这样,那就用老的成语吧。如果你现有的习惯用法看起来不错,那么不要觉得有任何压力去使用它--在我看来,它确实不错。例如,这将是可选的很好的用法:

代码语言:javascript
复制
item.next().map(Object::toString).ifPresent(System.out::println);

由于您需要在第一个非present可选参数上跳出循环,因此这并没有真正的帮助。

但是,我假设您真正感兴趣的是:在代码中利用Java 8的特性。您应该选择的抽象是Stream:

代码语言:javascript
复制
itemStream(() -> new Item(1)).forEach(item -> { ... all you need ... });

当然,你现在可以疯狂地进行流处理了:

代码语言:javascript
复制
itemStream(() -> new Item(1)).filter(item.nr > 3).mapToInt(Item::nr).sum();

下面是构建流的方式:

代码语言:javascript
复制
import java.util.Spliterators;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class ItemSpliterator extends Spliterators.AbstractSpliterator<Item>
{
  private Supplier<Item> supplyFirst;
  private Item lastItem;

  public ItemSpliterator(Supplier<Item> supplyFirst) {
    super(Long.MAX_VALUE, ORDERED | NONNULL);
    this.supplyFirst = supplyFirst;
  }

  @Override public boolean tryAdvance(Consumer<? super Item> action) {
    Item item;
    if ((item = lastItem) != null)
      item = lastItem = item.next();
    else if (supplyFirst != null) {
      item = lastItem = supplyFirst.get();
      supplyFirst = null;
    }
    else return false;
    if (item != null) {
      action.accept(item);
      return true;
    }
    return false;
  }

  public static Stream<Item> itemStream(Supplier<Item> supplyFirst) {
    return StreamSupport.stream(new ItemSpliterator(supplyFirst), false);
  }
}

有了这一点,你离无缝并行计算的能力只差了一小步。由于您的项目流基本上是连续的,因此我建议在这个主题上查看我的blog post

票数 6
EN

Stack Overflow用户

发布于 2015-02-26 21:11:46

只需将循环支持添加到您的API:

代码语言:javascript
复制
class Item {
    int nr;

    Item(int nr) {
        this.nr = nr;
        // an expensive operation
    }

    public void forEach(Consumer<Item> action) {
        for(Item i=this; ; i=new Item(i.nr + 1)) {
            action.accept(i);
            if(!someCondition) break;
        }
    }
    public Optional<Item> next() {
        return someCondition? Optional.of(new Item(nr+1)): Optional.empty();
    }
}

然后,您可以简单地通过lambda表达式进行迭代

代码语言:javascript
复制
    i.forEach(item -> {whatever you want to do with the item});

或方法引用

代码语言:javascript
复制
    i.forEach(System.out::println);

如果您希望支持比forEach循环更复杂的操作,那么supporting streams是正确的选择。它的相似之处在于,您的实现封装了如何在Item上迭代。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28741673

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档