-
Notifications
You must be signed in to change notification settings - Fork 242
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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. | ||
|
@@ -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; | ||
} | ||
|
||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ahah, there is exactly such a method. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
} | ||
} |
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); | ||
} | ||
} |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.