Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Utility methods on CloseableIterator to make life a little easier. #589

Merged
merged 2 commits into from
Apr 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions src/java/htsjdk/samtools/util/CloseableIterator.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@
package htsjdk.samtools.util;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
* This interface is used by iterators that use releasable resources during iteration.
Expand All @@ -36,10 +42,22 @@
* 2) When hasNext() returns false, the iterator implementation should automatically close itself.
* The latter makes it somewhat safer for consumers to use the for loop syntax for iteration:
* for (Type obj : getCloseableIterator()) { ... }
*
* We do not inherit from java.io.Closeable because IOExceptions are a pain to deal with.
*/
public interface CloseableIterator<T> extends Iterator<T>, Closeable {
/** Should be implemented to close/release any underlying resources. */
void close();

/** Consumes the contents of the iterator and returns it as a List. */
default List<T> toList() {
final List<T> list = new ArrayList<>();
while (hasNext()) list.add(next());
close();
return list;
Copy link
Member

@lbergelson lbergelson Apr 27, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this close the iterator before returning the list?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. I'll fix that.

}

public void close();
/** Returns a Stream that will consume from the underlying iterator. */
default Stream<T> stream() {
final Spliterator<T> s = Spliterators.spliteratorUnknownSize(this, Spliterator.ORDERED);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tfenne Is there any way that we can set it up so the iterator is automatically closed when the stream is consumed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree it would be nice, but I'm not sure how possible it is without a bunch of wrapper-ing of the iterators. Let me see if I can come up with something that's not too ugly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see an easy way unless stream offers some sort of on-closed callback... I was hoping you'd know some magic...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahah, there is exactly such a method. Stream.onClose(Runnable)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup - I just found that too. Updated to use it.

return StreamSupport.stream(s, false).onClose(this::close);
}
}
31 changes: 31 additions & 0 deletions src/tests/java/htsjdk/samtools/util/CloseableIteratorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package htsjdk.samtools.util;

import org.testng.Assert;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class CloseableIteratorTest {
@Test
public void testToList() {
final List<Integer> expected = Arrays.asList(1,2,3,4,5);
final PeekableIterator<Integer> peeky = new PeekableIterator<>(expected.iterator());
final List<Integer> actual = peeky.toList();

Assert.assertEquals(actual, expected);
Assert.assertEquals(peeky.toList(), new ArrayList<>()); // Should be empty the second time
}

@Test
public void testToStream() {
final List<Integer> inputs = Arrays.asList(1,2,3,4,5);
final PeekableIterator<Integer> peeky = new PeekableIterator<>(inputs.iterator());
final List<Integer> expected = inputs.stream().map(i -> i*2).collect(Collectors.toList());
final List<Integer> actual = peeky.stream().map(i -> i*2).collect(Collectors.toList());

Assert.assertEquals(actual, expected);
}
}