1. /*
  2. * Copyright 2001-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.buffer;
  17. import java.util.AbstractCollection;
  18. import java.util.Comparator;
  19. import java.util.Iterator;
  20. import java.util.NoSuchElementException;
  21. import org.apache.commons.collections.Buffer;
  22. import org.apache.commons.collections.BufferUnderflowException;
  23. /**
  24. * Binary heap implementation of <code>Buffer</code> that provides for
  25. * removal based on <code>Comparator</code> ordering.
  26. * <p>
  27. * The removal order of a binary heap is based on either the natural sort
  28. * order of its elements or a specified {@link Comparator}. The
  29. * {@link #remove()} method always returns the first element as determined
  30. * by the sort order. (The <code>ascendingOrder</code> flag in the constructors
  31. * can be used to reverse the sort order, in which case {@link #remove()}
  32. * will always remove the last element.) The removal order is
  33. * <i>not</i> the same as the order of iteration; elements are
  34. * returned by the iterator in no particular order.
  35. * <p>
  36. * The {@link #add(Object)} and {@link #remove()} operations perform
  37. * in logarithmic time. The {@link #get()} operation performs in constant
  38. * time. All other operations perform in linear time or worse.
  39. * <p>
  40. * Note that this implementation is not synchronized. Use
  41. * {@link org.apache.commons.collections.BufferUtils#synchronizedBuffer(Buffer)} or
  42. * {@link org.apache.commons.collections.buffer.SynchronizedBuffer#decorate(Buffer)}
  43. * to provide synchronized access to a <code>PriorityBuffer</code>:
  44. *
  45. * <pre>
  46. * Buffer heap = SynchronizedBuffer.decorate(new PriorityBuffer());
  47. * </pre>
  48. *
  49. * @since Commons Collections 3.0 (previously BinaryHeap v1.0)
  50. * @version $Revision: 1.5 $ $Date: 2004/05/15 12:33:23 $
  51. *
  52. * @author Peter Donald
  53. * @author Ram Chidambaram
  54. * @author Michael A. Smith
  55. * @author Paul Jack
  56. * @author Stephen Colebourne
  57. */
  58. public class PriorityBuffer extends AbstractCollection implements Buffer {
  59. /**
  60. * The default capacity for the buffer.
  61. */
  62. private static final int DEFAULT_CAPACITY = 13;
  63. /**
  64. * The elements in this buffer.
  65. */
  66. protected Object[] elements;
  67. /**
  68. * The number of elements currently in this buffer.
  69. */
  70. protected int size;
  71. /**
  72. * If true, the first element as determined by the sort order will
  73. * be returned. If false, the last element as determined by the
  74. * sort order will be returned.
  75. */
  76. protected boolean ascendingOrder;
  77. /**
  78. * The comparator used to order the elements
  79. */
  80. protected Comparator comparator;
  81. //-----------------------------------------------------------------------
  82. /**
  83. * Constructs a new empty buffer that sorts in ascending order by the
  84. * natural order of the objects added.
  85. */
  86. public PriorityBuffer() {
  87. this(DEFAULT_CAPACITY, true, null);
  88. }
  89. /**
  90. * Constructs a new empty buffer that sorts in ascending order using the
  91. * specified comparator.
  92. *
  93. * @param comparator the comparator used to order the elements,
  94. * null means use natural order
  95. */
  96. public PriorityBuffer(Comparator comparator) {
  97. this(DEFAULT_CAPACITY, true, comparator);
  98. }
  99. /**
  100. * Constructs a new empty buffer specifying the sort order and using the
  101. * natural order of the objects added.
  102. *
  103. * @param ascendingOrder if <code>true</code> the heap is created as a
  104. * minimum heap; otherwise, the heap is created as a maximum heap
  105. */
  106. public PriorityBuffer(boolean ascendingOrder) {
  107. this(DEFAULT_CAPACITY, ascendingOrder, null);
  108. }
  109. /**
  110. * Constructs a new empty buffer specifying the sort order and comparator.
  111. *
  112. * @param ascendingOrder true to use the order imposed by the given
  113. * comparator; false to reverse that order
  114. * @param comparator the comparator used to order the elements,
  115. * null means use natural order
  116. */
  117. public PriorityBuffer(boolean ascendingOrder, Comparator comparator) {
  118. this(DEFAULT_CAPACITY, ascendingOrder, comparator);
  119. }
  120. /**
  121. * Constructs a new empty buffer that sorts in ascending order by the
  122. * natural order of the objects added, specifying an initial capacity.
  123. *
  124. * @param capacity the initial capacity for the buffer, greater than zero
  125. * @throws IllegalArgumentException if <code>capacity</code> is <= <code>0</code>
  126. */
  127. public PriorityBuffer(int capacity) {
  128. this(capacity, true, null);
  129. }
  130. /**
  131. * Constructs a new empty buffer that sorts in ascending order using the
  132. * specified comparator and initial capacity.
  133. *
  134. * @param capacity the initial capacity for the buffer, greater than zero
  135. * @param comparator the comparator used to order the elements,
  136. * null means use natural order
  137. * @throws IllegalArgumentException if <code>capacity</code> is <= <code>0</code>
  138. */
  139. public PriorityBuffer(int capacity, Comparator comparator) {
  140. this(capacity, true, comparator);
  141. }
  142. /**
  143. * Constructs a new empty buffer that specifying initial capacity and
  144. * sort order, using the natural order of the objects added.
  145. *
  146. * @param capacity the initial capacity for the buffer, greater than zero
  147. * @param ascendingOrder if <code>true</code> the heap is created as a
  148. * minimum heap; otherwise, the heap is created as a maximum heap.
  149. * @throws IllegalArgumentException if <code>capacity</code> is <code><= 0</code>
  150. */
  151. public PriorityBuffer(int capacity, boolean ascendingOrder) {
  152. this(capacity, ascendingOrder, null);
  153. }
  154. /**
  155. * Constructs a new empty buffer that specifying initial capacity,
  156. * sort order and comparator.
  157. *
  158. * @param capacity the initial capacity for the buffer, greater than zero
  159. * @param ascendingOrder true to use the order imposed by the given
  160. * comparator; false to reverse that order
  161. * @param comparator the comparator used to order the elements,
  162. * null means use natural order
  163. * @throws IllegalArgumentException if <code>capacity</code> is <code><= 0</code>
  164. */
  165. public PriorityBuffer(int capacity, boolean ascendingOrder, Comparator comparator) {
  166. super();
  167. if (capacity <= 0) {
  168. throw new IllegalArgumentException("invalid capacity");
  169. }
  170. this.ascendingOrder = ascendingOrder;
  171. //+1 as 0 is noop
  172. this.elements = new Object[capacity + 1];
  173. this.comparator = comparator;
  174. }
  175. //-----------------------------------------------------------------------
  176. /**
  177. * Checks whether the heap is ascending or descending order.
  178. *
  179. * @return true if ascending order (a min heap)
  180. */
  181. public boolean isAscendingOrder() {
  182. return ascendingOrder;
  183. }
  184. /**
  185. * Gets the comparator being used for this buffer, null is natural order.
  186. *
  187. * @return the comparator in use, null is natural order
  188. */
  189. public Comparator comparator() {
  190. return comparator;
  191. }
  192. //-----------------------------------------------------------------------
  193. /**
  194. * Returns the number of elements in this buffer.
  195. *
  196. * @return the number of elements in this buffer
  197. */
  198. public int size() {
  199. return size;
  200. }
  201. /**
  202. * Clears all elements from the buffer.
  203. */
  204. public void clear() {
  205. elements = new Object[elements.length]; // for gc
  206. size = 0;
  207. }
  208. /**
  209. * Adds an element to the buffer.
  210. * <p>
  211. * The element added will be sorted according to the comparator in use.
  212. *
  213. * @param element the element to be added
  214. * @return true always
  215. */
  216. public boolean add(Object element) {
  217. if (isAtCapacity()) {
  218. grow();
  219. }
  220. // percolate element to it's place in tree
  221. if (ascendingOrder) {
  222. percolateUpMinHeap(element);
  223. } else {
  224. percolateUpMaxHeap(element);
  225. }
  226. return true;
  227. }
  228. /**
  229. * Gets the next element to be removed without actually removing it (peek).
  230. *
  231. * @return the next element
  232. * @throws BufferUnderflowException if the buffer is empty
  233. */
  234. public Object get() {
  235. if (isEmpty()) {
  236. throw new BufferUnderflowException();
  237. } else {
  238. return elements[1];
  239. }
  240. }
  241. /**
  242. * Gets and removes the next element (pop).
  243. *
  244. * @return the next element
  245. * @throws BufferUnderflowException if the buffer is empty
  246. */
  247. public Object remove() {
  248. final Object result = get();
  249. elements[1] = elements[size--];
  250. // set the unused element to 'null' so that the garbage collector
  251. // can free the object if not used anywhere else.(remove reference)
  252. elements[size + 1] = null;
  253. if (size != 0) {
  254. // percolate top element to it's place in tree
  255. if (ascendingOrder) {
  256. percolateDownMinHeap(1);
  257. } else {
  258. percolateDownMaxHeap(1);
  259. }
  260. }
  261. return result;
  262. }
  263. //-----------------------------------------------------------------------
  264. /**
  265. * Tests if the buffer is at capacity.
  266. *
  267. * @return <code>true</code> if buffer is full; <code>false</code> otherwise.
  268. */
  269. protected boolean isAtCapacity() {
  270. //+1 as element 0 is noop
  271. return elements.length == size + 1;
  272. }
  273. /**
  274. * Percolates element down heap from the position given by the index.
  275. * <p>
  276. * Assumes it is a minimum heap.
  277. *
  278. * @param index the index for the element
  279. */
  280. protected void percolateDownMinHeap(final int index) {
  281. final Object element = elements[index];
  282. int hole = index;
  283. while ((hole * 2) <= size) {
  284. int child = hole * 2;
  285. // if we have a right child and that child can not be percolated
  286. // up then move onto other child
  287. if (child != size && compare(elements[child + 1], elements[child]) < 0) {
  288. child++;
  289. }
  290. // if we found resting place of bubble then terminate search
  291. if (compare(elements[child], element) >= 0) {
  292. break;
  293. }
  294. elements[hole] = elements[child];
  295. hole = child;
  296. }
  297. elements[hole] = element;
  298. }
  299. /**
  300. * Percolates element down heap from the position given by the index.
  301. * <p>
  302. * Assumes it is a maximum heap.
  303. *
  304. * @param index the index of the element
  305. */
  306. protected void percolateDownMaxHeap(final int index) {
  307. final Object element = elements[index];
  308. int hole = index;
  309. while ((hole * 2) <= size) {
  310. int child = hole * 2;
  311. // if we have a right child and that child can not be percolated
  312. // up then move onto other child
  313. if (child != size && compare(elements[child + 1], elements[child]) > 0) {
  314. child++;
  315. }
  316. // if we found resting place of bubble then terminate search
  317. if (compare(elements[child], element) <= 0) {
  318. break;
  319. }
  320. elements[hole] = elements[child];
  321. hole = child;
  322. }
  323. elements[hole] = element;
  324. }
  325. /**
  326. * Percolates element up heap from the position given by the index.
  327. * <p>
  328. * Assumes it is a minimum heap.
  329. *
  330. * @param index the index of the element to be percolated up
  331. */
  332. protected void percolateUpMinHeap(final int index) {
  333. int hole = index;
  334. Object element = elements[hole];
  335. while (hole > 1 && compare(element, elements[hole / 2]) < 0) {
  336. // save element that is being pushed down
  337. // as the element "bubble" is percolated up
  338. final int next = hole / 2;
  339. elements[hole] = elements[next];
  340. hole = next;
  341. }
  342. elements[hole] = element;
  343. }
  344. /**
  345. * Percolates a new element up heap from the bottom.
  346. * <p>
  347. * Assumes it is a minimum heap.
  348. *
  349. * @param element the element
  350. */
  351. protected void percolateUpMinHeap(final Object element) {
  352. elements[++size] = element;
  353. percolateUpMinHeap(size);
  354. }
  355. /**
  356. * Percolates element up heap from from the position given by the index.
  357. * <p>
  358. * Assume it is a maximum heap.
  359. *
  360. * @param index the index of the element to be percolated up
  361. */
  362. protected void percolateUpMaxHeap(final int index) {
  363. int hole = index;
  364. Object element = elements[hole];
  365. while (hole > 1 && compare(element, elements[hole / 2]) > 0) {
  366. // save element that is being pushed down
  367. // as the element "bubble" is percolated up
  368. final int next = hole / 2;
  369. elements[hole] = elements[next];
  370. hole = next;
  371. }
  372. elements[hole] = element;
  373. }
  374. /**
  375. * Percolates a new element up heap from the bottom.
  376. * <p>
  377. * Assume it is a maximum heap.
  378. *
  379. * @param element the element
  380. */
  381. protected void percolateUpMaxHeap(final Object element) {
  382. elements[++size] = element;
  383. percolateUpMaxHeap(size);
  384. }
  385. /**
  386. * Compares two objects using the comparator if specified, or the
  387. * natural order otherwise.
  388. *
  389. * @param a the first object
  390. * @param b the second object
  391. * @return -ve if a less than b, 0 if they are equal, +ve if a greater than b
  392. */
  393. protected int compare(Object a, Object b) {
  394. if (comparator != null) {
  395. return comparator.compare(a, b);
  396. } else {
  397. return ((Comparable) a).compareTo(b);
  398. }
  399. }
  400. /**
  401. * Increases the size of the heap to support additional elements
  402. */
  403. protected void grow() {
  404. final Object[] array = new Object[elements.length * 2];
  405. System.arraycopy(elements, 0, array, 0, elements.length);
  406. elements = array;
  407. }
  408. //-----------------------------------------------------------------------
  409. /**
  410. * Returns an iterator over this heap's elements.
  411. *
  412. * @return an iterator over this heap's elements
  413. */
  414. public Iterator iterator() {
  415. return new Iterator() {
  416. private int index = 1;
  417. private int lastReturnedIndex = -1;
  418. public boolean hasNext() {
  419. return index <= size;
  420. }
  421. public Object next() {
  422. if (!hasNext()) {
  423. throw new NoSuchElementException();
  424. }
  425. lastReturnedIndex = index;
  426. index++;
  427. return elements[lastReturnedIndex];
  428. }
  429. public void remove() {
  430. if (lastReturnedIndex == -1) {
  431. throw new IllegalStateException();
  432. }
  433. elements[ lastReturnedIndex ] = elements[ size ];
  434. elements[ size ] = null;
  435. size--;
  436. if( size != 0 && lastReturnedIndex <= size) {
  437. int compareToParent = 0;
  438. if (lastReturnedIndex > 1) {
  439. compareToParent = compare(elements[lastReturnedIndex],
  440. elements[lastReturnedIndex / 2]);
  441. }
  442. if (ascendingOrder) {
  443. if (lastReturnedIndex > 1 && compareToParent < 0) {
  444. percolateUpMinHeap(lastReturnedIndex);
  445. } else {
  446. percolateDownMinHeap(lastReturnedIndex);
  447. }
  448. } else { // max heap
  449. if (lastReturnedIndex > 1 && compareToParent > 0) {
  450. percolateUpMaxHeap(lastReturnedIndex);
  451. } else {
  452. percolateDownMaxHeap(lastReturnedIndex);
  453. }
  454. }
  455. }
  456. index--;
  457. lastReturnedIndex = -1;
  458. }
  459. };
  460. }
  461. /**
  462. * Returns a string representation of this heap. The returned string
  463. * is similar to those produced by standard JDK collections.
  464. *
  465. * @return a string representation of this heap
  466. */
  467. public String toString() {
  468. final StringBuffer sb = new StringBuffer();
  469. sb.append("[ ");
  470. for (int i = 1; i < size + 1; i++) {
  471. if (i != 1) {
  472. sb.append(", ");
  473. }
  474. sb.append(elements[i]);
  475. }
  476. sb.append(" ]");
  477. return sb.toString();
  478. }
  479. }