package org.csstudio.domain.common.collection; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import javax.annotation.Nonnull; /** * Helper class to divide a collection into iterable chunks. * You set the chunk size and the collection, you get an iterator whose elements are subcollections. * * @author jpenning * @author $Author: jpenning $ * @version $Revision: 1.1 $ * @since 31.08.2010 * * @param <E> type of collection elements */ public class ChunkableCollection<E> implements Iterable<Collection<E>> { private final Collection<E> _collection; private final int _chunkSize; private final int _chunkCount; private int _currentChunk; public ChunkableCollection(@Nonnull final Collection<E> collection, final int chunkSize) { assert chunkSize > 0 : "chunkSize must be > 0, but was " + chunkSize; _collection = collection; _chunkSize = chunkSize; _currentChunk = 0; _chunkCount = calcChunkCount(collection.size(), chunkSize); } @SuppressWarnings("synthetic-access") @Override @Nonnull public final Iterator<Collection<E>> iterator() { return new MyIterator(); } // package scoped for test final int calcChunkCount(int wholeSize, int chunkSize) { assert wholeSize >= 0 : "wholeSize must be >= 0, but was " + wholeSize; assert chunkSize >= 0 : "chunkSize must be >= 0, but was " + chunkSize; int result = 0; if (wholeSize == 0) { result = 0; } else if (chunkSize == 0) { result = 1; } else { result = wholeSize / chunkSize; if (wholeSize % chunkSize > 0) { result++; } } return result; } /** * the iterator delivers subcollections as elements */ @SuppressWarnings("synthetic-access") private class MyIterator implements Iterator<Collection<E>> { @Override public boolean hasNext() { return _currentChunk < _chunkCount; } @Override public Collection<E> next() { Collection<E> result = new ArrayList<E>(_chunkSize); Iterator<E> iter = _collection.iterator(); skipAlreadyProcessedChunks(iter); copyOneChunk(iter, result); _currentChunk++; return result; } private void skipAlreadyProcessedChunks(@Nonnull final Iterator<E> iter) { int to = _currentChunk * _chunkSize; for (int i = 0; i < to; i++) { iter.next(); } } private void copyOneChunk(@Nonnull final Iterator<E> iter, @Nonnull final Collection<E> result) { for (int i = 0; i < calcChunkSize(); i++) { result.add(iter.next()); } } private int calcChunkSize() { int result = _chunkSize; if ( (_currentChunk + 1) == _chunkCount) { int rest = _collection.size() % _chunkSize; result = rest == 0 ? _chunkSize : rest; } return result; } @Override public void remove() { throw new UnsupportedOperationException("Iterator of " + ChunkableCollection.class.getName() + " cannot remove"); } } }