1. /*
  2. * Copyright 2002-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.apache.commons.collections;
  17. import java.util.AbstractCollection;
  18. import java.util.Iterator;
  19. import java.util.NoSuchElementException;
  20. /**
  21. * UnboundedFifoBuffer is a very efficient buffer implementation.
  22. * According to performance testing, it exhibits a constant access time, but it
  23. * also outperforms ArrayList when used for the same purpose.
  24. * <p>
  25. * The removal order of an <code>UnboundedFifoBuffer</code> is based on the insertion
  26. * order; elements are removed in the same order in which they were added.
  27. * The iteration order is the same as the removal order.
  28. * <p>
  29. * The {@link #remove()} and {@link #get()} operations perform in constant time.
  30. * The {@link #add(Object)} operation performs in amortized constant time. All
  31. * other operations perform in linear time or worse.
  32. * <p>
  33. * Note that this implementation is not synchronized. The following can be
  34. * used to provide synchronized access to your <code>UnboundedFifoBuffer</code>:
  35. * <pre>
  36. * Buffer fifo = BufferUtils.synchronizedBuffer(new UnboundedFifoBuffer());
  37. * </pre>
  38. * <p>
  39. * This buffer prevents null objects from being added.
  40. *
  41. * @deprecated Moved to buffer subpackage. Due to be removed in v4.0.
  42. * @since Commons Collections 2.1
  43. * @version $Revision: 1.15 $ $Date: 2004/02/18 01:15:42 $
  44. *
  45. * @author Avalon
  46. * @author Federico Barbieri
  47. * @author Berin Loritsch
  48. * @author Paul Jack
  49. * @author Stephen Colebourne
  50. */
  51. public class UnboundedFifoBuffer extends AbstractCollection implements Buffer {
  52. protected Object[] m_buffer;
  53. protected int m_head;
  54. protected int m_tail;
  55. /**
  56. * Constructs an UnboundedFifoBuffer with the default number of elements.
  57. * It is exactly the same as performing the following:
  58. *
  59. * <pre>
  60. * new UnboundedFifoBuffer(32);
  61. * </pre>
  62. */
  63. public UnboundedFifoBuffer() {
  64. this(32);
  65. }
  66. /**
  67. * Constructs an UnboundedFifoBuffer with the specified number of elements.
  68. * The integer must be a positive integer.
  69. *
  70. * @param initialSize the initial size of the buffer
  71. * @throws IllegalArgumentException if the size is less than 1
  72. */
  73. public UnboundedFifoBuffer(int initialSize) {
  74. if (initialSize <= 0) {
  75. throw new IllegalArgumentException("The size must be greater than 0");
  76. }
  77. m_buffer = new Object[initialSize + 1];
  78. m_head = 0;
  79. m_tail = 0;
  80. }
  81. /**
  82. * Returns the number of elements stored in the buffer.
  83. *
  84. * @return this buffer's size
  85. */
  86. public int size() {
  87. int size = 0;
  88. if (m_tail < m_head) {
  89. size = m_buffer.length - m_head + m_tail;
  90. } else {
  91. size = m_tail - m_head;
  92. }
  93. return size;
  94. }
  95. /**
  96. * Returns true if this buffer is empty; false otherwise.
  97. *
  98. * @return true if this buffer is empty
  99. */
  100. public boolean isEmpty() {
  101. return (size() == 0);
  102. }
  103. /**
  104. * Adds the given element to this buffer.
  105. *
  106. * @param obj the element to add
  107. * @return true, always
  108. * @throws NullPointerException if the given element is null
  109. * @throws BufferOverflowException if this buffer is full
  110. */
  111. public boolean add(final Object obj) {
  112. if (obj == null) {
  113. throw new NullPointerException("Attempted to add null object to buffer");
  114. }
  115. if (size() + 1 >= m_buffer.length) {
  116. Object[] tmp = new Object[((m_buffer.length - 1) * 2) + 1];
  117. int j = 0;
  118. for (int i = m_head; i != m_tail;) {
  119. tmp[j] = m_buffer[i];
  120. m_buffer[i] = null;
  121. j++;
  122. i++;
  123. if (i == m_buffer.length) {
  124. i = 0;
  125. }
  126. }
  127. m_buffer = tmp;
  128. m_head = 0;
  129. m_tail = j;
  130. }
  131. m_buffer[m_tail] = obj;
  132. m_tail++;
  133. if (m_tail >= m_buffer.length) {
  134. m_tail = 0;
  135. }
  136. return true;
  137. }
  138. /**
  139. * Returns the next object in the buffer.
  140. *
  141. * @return the next object in the buffer
  142. * @throws BufferUnderflowException if this buffer is empty
  143. */
  144. public Object get() {
  145. if (isEmpty()) {
  146. throw new BufferUnderflowException("The buffer is already empty");
  147. }
  148. return m_buffer[m_head];
  149. }
  150. /**
  151. * Removes the next object from the buffer
  152. *
  153. * @return the removed object
  154. * @throws BufferUnderflowException if this buffer is empty
  155. */
  156. public Object remove() {
  157. if (isEmpty()) {
  158. throw new BufferUnderflowException("The buffer is already empty");
  159. }
  160. Object element = m_buffer[m_head];
  161. if (null != element) {
  162. m_buffer[m_head] = null;
  163. m_head++;
  164. if (m_head >= m_buffer.length) {
  165. m_head = 0;
  166. }
  167. }
  168. return element;
  169. }
  170. /**
  171. * Increments the internal index.
  172. *
  173. * @param index the index to increment
  174. * @return the updated index
  175. */
  176. private int increment(int index) {
  177. index++;
  178. if (index >= m_buffer.length) {
  179. index = 0;
  180. }
  181. return index;
  182. }
  183. /**
  184. * Decrements the internal index.
  185. *
  186. * @param index the index to decrement
  187. * @return the updated index
  188. */
  189. private int decrement(int index) {
  190. index--;
  191. if (index < 0) {
  192. index = m_buffer.length - 1;
  193. }
  194. return index;
  195. }
  196. /**
  197. * Returns an iterator over this buffer's elements.
  198. *
  199. * @return an iterator over this buffer's elements
  200. */
  201. public Iterator iterator() {
  202. return new Iterator() {
  203. private int index = m_head;
  204. private int lastReturnedIndex = -1;
  205. public boolean hasNext() {
  206. return index != m_tail;
  207. }
  208. public Object next() {
  209. if (!hasNext())
  210. throw new NoSuchElementException();
  211. lastReturnedIndex = index;
  212. index = increment(index);
  213. return m_buffer[lastReturnedIndex];
  214. }
  215. public void remove() {
  216. if (lastReturnedIndex == -1)
  217. throw new IllegalStateException();
  218. // First element can be removed quickly
  219. if (lastReturnedIndex == m_head) {
  220. UnboundedFifoBuffer.this.remove();
  221. lastReturnedIndex = -1;
  222. return;
  223. }
  224. // Other elements require us to shift the subsequent elements
  225. int i = lastReturnedIndex + 1;
  226. while (i != m_tail) {
  227. if (i >= m_buffer.length) {
  228. m_buffer[i - 1] = m_buffer[0];
  229. i = 0;
  230. } else {
  231. m_buffer[i - 1] = m_buffer[i];
  232. i++;
  233. }
  234. }
  235. lastReturnedIndex = -1;
  236. m_tail = decrement(m_tail);
  237. m_buffer[m_tail] = null;
  238. index = decrement(index);
  239. }
  240. };
  241. }
  242. }