// Copyright 2017 JanusGraph Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.janusgraph.graphdb.query; import com.google.common.base.Preconditions; import java.util.Iterator; import java.util.NoSuchElementException; /** * An iterator implementation that wraps around another iterator to iterate it up to a given limit. * The idea is that the wrapped iterator is based on data that is fairly expensive to retrieve (e.g. from a database). * As such, we don't want to retrieve all of it but "just enough". However, if more data is requested, then we want * the wrapped iterator to be updated (i.e. additional data be retrieved). * </p> * The limit for the wrapped iterator is updated by a factor of 2. When the iterator is updated, the iterator must be * iterated through to the point of the last returned element. While this may seem expensive, it is less expensive than * retrieving more than needed elements in the first place. However, this still means the initial currentLimit in the * constructor should be chosen wisely. * * @author Matthias Broecheler (me@matthiasb.com) */ public abstract class LimitAdjustingIterator<R> implements Iterator<R> { private final int maxLimit; private int currentLimit; private int count; private Iterator<R> iter; /** * Initializes this iterator with the current limit and the maximum number of elements that may be retrieved from the * wrapped iterator. * * @param maxLimit * @param currentLimit */ public LimitAdjustingIterator(final int maxLimit, final int currentLimit) { Preconditions.checkArgument(currentLimit>0 && maxLimit>0,"Invalid limits: current [%s], max [%s]",currentLimit,maxLimit); this.currentLimit = currentLimit; this.maxLimit = maxLimit; this.count = 0; this.iter = null; } /** * This returns the wrapped iterator with up to the specified number of elements. * * @param newLimit * @return */ public abstract Iterator<R> getNewIterator(int newLimit); @Override public boolean hasNext() { if (iter==null) iter = getNewIterator(currentLimit); if (count < currentLimit) return iter.hasNext(); if (currentLimit>=maxLimit) return false; //Get an iterator with an updated limit currentLimit = (int) Math.min(maxLimit, Math.round(currentLimit * 2.0)); iter = getNewIterator(currentLimit); /* We need to iterate out the iterator to the point where we last left of. This is pretty expensive and hence it should be ensured that the initial limit is a good guesstimate. */ for (int i = 0; i < count; i++) iter.next(); assert count < currentLimit : count + " vs " + currentLimit + " | " + maxLimit; return hasNext(); } @Override public R next() { if (!hasNext()) throw new NoSuchElementException(); count++; return iter.next(); } @Override public void remove() { throw new UnsupportedOperationException(); } }