在未读取所有元素时清理Iterable

时间:2016-08-30 02:09:23

标签: java rx-java reactive-programming java-6

在RxJava上弄湿我的脚。我有一个实现Iterable的类我要转换为Observable。使用Observable.from()似乎很容易。但是,我需要设置和拆除为我提供单个条目的代码(迭代器中的next()

当我浏览整个序列时,这很容易。我添加了对hasNext()函数的调用,当没有下一个函数时,我运行了拆解。然而,我想要使用的一个非常有前景的运算符是take(someNumber)。如果在Iterator用完项目之前停止,则清理代码永远不会运行。

我可以做些什么来清理我的运行?如果使用的不是from(Iterable),我就可以了。我现在坚持使用Java6。为了说明我的困境,我创建了一个最小的样本:

更新:根据不将Iterator和Iterable混合在一起的反馈,我更新了下面的代码。要了解原始答案,请original code is in that gist

更新了测试代码(仍然不错):

import rx.Observable;
import rx.functions.Action0;
import rx.functions.Action1;

/**
* @author stw
*
*/
public class RXTest {

/**
 * @param args
 */
public static void main(String[] args) {
  ComplicatedObject co = new ComplicatedObject();
  Observable<FancyObject> fancy = Observable.from(co);
  // if the take is less than the elements cleanup never
  // runs. If you take the take out, cleanup runs
  fancy.take(3).subscribe(
      new Action1<FancyObject>() {

        public void call(FancyObject item) {
            System.out.println(item.getName());
        }
    },
    new Action1<Throwable>() {

        public void call(Throwable error) {
            System.out.println("Error encountered: " + error.getMessage());
        }
    },
    new Action0() {

        public void call() {
            System.out.println("Sequence complete");
        }
    }

      );

}

}

花哨的对象:

import java.util.Date;
import java.util.UUID;

/**
* @author stw
*
*/
public class FancyObject  {
private String name = UUID.randomUUID().toString();
private Date created = new Date();
public String getName() {
  return this.name;
}
public void setName(String name) {
  this.name = name;
}
public Date getCreated() {
  return this.created;
}
public void setCreated(Date created) {
  this.created = created;
}
}

迭代器:

import java.util.Iterator;

/**
 * @author stw
 *
 */
public class FancyIterator implements Iterator<FancyObject> {

  private final ComplicatedObject theObject;
  private int fancyCount = 0;


  public FancyIterator(ComplicatedObject co) {
    this.theObject = co;
  }

  public boolean hasNext() {
    return this.theObject.hasObject(this.fancyCount);
   }


   public FancyObject next() {
     FancyObject result = this.theObject.getOne(this.fancyCount);
     this.fancyCount++;  
     return result;
   }

}

The Iterable:

import java.util.Iterator;
import java.util.Vector;

/**
 * @author stw
 *
 */
public class ComplicatedObject implements Iterable<FancyObject> {

  private boolean isInitialized = false;

  Vector<FancyObject> allOfThem = new Vector<FancyObject>();


  public Iterator<FancyObject> iterator() {
   return new FancyIterator(this);
  }

  public boolean hasObject(int whichone) {
    if (!this.isInitialized) {
      this.setupAccesstoFancyObject(); 
    }
    return (whichone < this.allOfThem.size());
  }
  public FancyObject getOne(int whichone) {
      if (!this.isInitialized) {
        this.setupAccesstoFancyObject();
      }
      if (whichone < this.allOfThem.size()) {
        return this.allOfThem.get(whichone);
      }
      // If we ask bejond...
      this.isInitialized = false;
      this.teardownAccessToFancyObjects();
      return null;
  }

  private void setupAccesstoFancyObject() {
    System.out.println("Initializing fancy objects");
    for (int i = 0; i < 20; i++) {
      this.allOfThem.addElement(new FancyObject());
    }
    this.isInitialized = true;
  }

  private void teardownAccessToFancyObjects() {
    System.out.println("I'm doing proper cleanup here");

  }

}

真正的问题(thx @Andreas)似乎是:

当底层代码需要设置/拆除时,我可以使用什么构造来创建Observable,尤其是当人们期望不是所有元素都被拉出时。 Iterable只是我的第一个想法

更新2 :根据Dave的回答I created a gist和我的工作解决方案。迭代器并不完美,但它是一个开始。

3 个答案:

答案 0 :(得分:2)

如果您需要这种控制,则需要将Iterable的实施与Iterator分开。 Iterable表示该类可以提供一个Iterator,它对于该类有意义的任何方式都是有意义的。

但是,如果您在同一个班级中实施Iterator,那么您只会为Iterator的每个实例只有一个ComplicatedObject。正确的方法是实施

class FancyObjectIterator implements Iterator<FancyObject>
{
    ...
}

ComplicatedObject分开,因此您只需在完成它们时就丢弃部分使用的迭代器。 ComplicatedObject应仅实施Iterable<FancyObject>

如果您反对该方法,因为迭代器具有更多需要特殊清理的状态,那么您的设计就会出现问题。 Iterator应该注意的唯一状态是基础“集合”中的当前位置,因为迭代器的概念可以应用于典型的“集合”和“位置”的非常宽松的定义。集合。

答案 1 :(得分:2)

Observable.using用于拆除终止(完成或错误)或取消订阅。要使用它,您需要使拆卸代码可访问,以便您的源可观察对象如下所示:

source = Observable.using(
    resourceFactory, 
    observableFactory, 
    resourceDisposer);

使用您的代码可能如下所示:

source = Observable.using(
    () -> new ComplicatedObject(),
    co -> Observable.from(co), 
    co -> co.tearDown());

答案 2 :(得分:1)

您无法同时实施IteratorIterable,因为Iterable.iterator()必须返回 Iterator或每次通话。

允许代码并行多次迭代相同的Iterable

示例:在Iterable中找到重复元素的过于简化的方法:

Iterable<MyObject> myIterable = ...;
for (MyObject myObj1 : myIterable) {
    for (MyObject myObj2 : myIterable) {
        if (myObj1 != myObj2 && myObj1.equals(myObj2)) {
            // found duplicate
        }
    }
}

此处使用的增强型for循环将分别使用Iterator

如您所见,每个Iterator必须保持自己的独立位置。因此,iterator()方法需要返回一个具有自己状态的新对象。

关于清理代码的问题,Iterator没有close()方法。迭代器状态不应该要求清理。如果他们绝对必须,终结器可以做到,但终结器可能需要非常长时间来调用。终结者的一般建议是:不要。