001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.collections4.iterators; 019 020import java.util.Iterator; 021import java.util.NoSuchElementException; 022 023/** 024 * Decorates an iterator to support one-element lookahead while iterating. 025 * <p> 026 * The decorator supports the removal operation, but an {@link IllegalStateException} 027 * will be thrown if {@link #remove()} is called directly after a call to 028 * {@link #peek()} or {@link #element()}. 029 * 030 * @since 4.0 031 * @version $Id: PeekingIterator.html 972421 2015-11-14 20:00:04Z tn $ 032 */ 033public class PeekingIterator<E> implements Iterator<E> { 034 035 /** The iterator being decorated. */ 036 private final Iterator<? extends E> iterator; 037 038 /** Indicates that the decorated iterator is exhausted. */ 039 private boolean exhausted = false; 040 041 /** Indicates if the lookahead slot is filled. */ 042 private boolean slotFilled = false; 043 044 /** The current slot for lookahead. */ 045 private E slot; 046 047 //----------------------------------------------------------------------- 048 /** 049 * Decorates the specified iterator to support one-element lookahead. 050 * <p> 051 * If the iterator is already a {@link PeekingIterator} it is returned directly. 052 * 053 * @param <E> the element type 054 * @param iterator the iterator to decorate 055 * @return a new peeking iterator 056 * @throws IllegalArgumentException if the iterator is null 057 */ 058 public static <E> PeekingIterator<E> peekingIterator(final Iterator<? extends E> iterator) { 059 if (iterator == null) { 060 throw new IllegalArgumentException("Iterator must not be null"); 061 } 062 if (iterator instanceof PeekingIterator<?>) { 063 @SuppressWarnings("unchecked") // safe cast 064 final PeekingIterator<E> it = (PeekingIterator<E>) iterator; 065 return it; 066 } 067 return new PeekingIterator<E>(iterator); 068 } 069 070 //----------------------------------------------------------------------- 071 072 /** 073 * Constructor. 074 * 075 * @param iterator the iterator to decorate 076 */ 077 public PeekingIterator(final Iterator<? extends E> iterator) { 078 this.iterator = iterator; 079 } 080 081 private void fill() { 082 if (exhausted || slotFilled) { 083 return; 084 } 085 if (iterator.hasNext()) { 086 slot = iterator.next(); 087 slotFilled = true; 088 } else { 089 exhausted = true; 090 slot = null; 091 slotFilled = false; 092 } 093 } 094 095 //----------------------------------------------------------------------- 096 public boolean hasNext() { 097 if (exhausted) { 098 return false; 099 } 100 return slotFilled ? true : iterator.hasNext(); 101 } 102 103 /** 104 * Returns the next element in iteration without advancing the underlying iterator. 105 * If the iterator is already exhausted, null will be returned. 106 * <p> 107 * Note: this method does not throw a {@link NoSuchElementException} if the iterator 108 * is already exhausted. If you want such a behavior, use {@link #element()} instead. 109 * <p> 110 * The rationale behind this is to follow the {@link java.util.Queue} interface 111 * which uses the same terminology. 112 * 113 * @return the next element from the iterator 114 */ 115 public E peek() { 116 fill(); 117 return exhausted ? null : slot; 118 } 119 120 /** 121 * Returns the next element in iteration without advancing the underlying iterator. 122 * If the iterator is already exhausted, null will be returned. 123 * 124 * @return the next element from the iterator 125 * @throws NoSuchElementException if the iterator is already exhausted according to {@link #hasNext()} 126 */ 127 public E element() { 128 fill(); 129 if (exhausted) { 130 throw new NoSuchElementException(); 131 } 132 return slot; 133 } 134 135 public E next() { 136 if (!hasNext()) { 137 throw new NoSuchElementException(); 138 } 139 final E x = slotFilled ? slot : iterator.next(); 140 // reset the lookahead slot 141 slot = null; 142 slotFilled = false; 143 return x; 144 } 145 146 /** 147 * {@inheritDoc} 148 * 149 * @throws IllegalStateException if {@link #peek()} or {@link #element()} has been called 150 * prior to the call to {@link #remove()} 151 */ 152 public void remove() { 153 if (slotFilled) { 154 throw new IllegalStateException(); 155 } 156 iterator.remove(); 157 } 158 159}