All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.epam.deltix.data.stream.pq.PriorityQueueExt Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2023 EPAM Systems, Inc
 *
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership. 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 com.epam.deltix.data.stream.pq;

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.io.PrintStream;

/**
 * Binary heap-based priority queue.
 * 

Optimizations (compared to {@link PriorityQueueExt}): * *

    *
  • We not extract keys from objects when comparing them. Instead we store keys in separate array.
  • * *
  • When root removed, we don't update tree structure immediately. * Instead we mark that root is missing and update structure
  • *
*/ @ParametersAreNonnullByDefault public final class PriorityQueueExt { /** * Number of elements in the heap. */ private int mSize; /** * The heap represented by array. Root has the index of 1. * Left child of node A is at A << 1. Right child of node * A is at A << 1 + 1. Parent of node A is at A >> 1. */ private Object[] heap; private long[] keys; private final byte direction; // 1 for ascending and -1 for descending private boolean rootMissing = false; // If true, then the root element should be considered empty. public PriorityQueueExt(int capacity, boolean ascending) { init (capacity); this.direction = (byte) (ascending ? 1 : -1); } public void dump (PrintStream ps) { dump (ps, "", 1); } public void dump (PrintStream ps, String indent, int pos) { T obj = getInternal (pos); ps.println (indent + pos + ": " + obj); int leftChildPos = pos << 1; int rightChildPos = leftChildPos + 1; if (leftChildPos <= mSize) dump (ps, indent + " ", leftChildPos); if (rightChildPos <= mSize) dump (ps, indent + " ", rightChildPos); } private void init (int numObjects) { heap = new Object[numObjects + 1]; keys = new long[numObjects + 1]; mSize = 0; } public void clear () { mSize = 0; rootMissing = false; } public int size () { return rootMissing ? mSize - 1 : mSize; } public boolean isEmpty () { return mSize == 0 || (rootMissing && mSize == 1); } /** * Spec: set heap at pos to obj, * and percolate the element up if necessary. */ private int percUp(int pos, T obj, long objKey) { if (pos > 1) { pos = findPosForElementInPercUp(pos, objKey); } heap [pos] = obj; keys [pos] = objKey; return pos; } /** * This code is moved to a separate method from {@link #percUp} to allow JIT not inline this method. */ private int findPosForElementInPercUp(int pos, long objKey) { // Cache fields Object[] heap = this.heap; long[] keys = this.keys; byte direction = this.direction; while (pos > 1) { int parentPos = pos >> 1; long parentKey = keys[parentPos]; if (Long.compare(parentKey, objKey) * direction <= 0) break; /** * Percolate the parent down into the hole */ heap[pos] = heap[parentPos]; keys[pos] = keys[parentPos]; pos = parentPos; } return pos; } /** * Spec: set heap at pos to id, * and percolate the element down if necessary. */ private int percDown(int pos, T obj, long objKey) { if (pos * 2 <= mSize) { pos = findPosForElementInPercDown(pos, objKey); } /** * Plug this hole with the last element. */ heap [pos] = obj; keys[pos] = objKey; return pos; } /** * This code is moved to a separate method from {@link #percDown} to allow JIT not inline this method. */ private int findPosForElementInPercDown(int pos, long objKey) { // Cache fields int mSize = this.mSize; Object[] heap = this.heap; long[] keys = this.keys; byte direction = this.direction; for (; ; ) { int leftChildPos = pos << 1; if (leftChildPos > mSize) break; /** * Find smallest child of the parent at pos. */ long leftKey = keys[leftChildPos]; int rightChildPos = leftChildPos + 1; int smallestChildPos; long smallestChildKey; if (rightChildPos <= mSize) { // Node has both left and right chilren. long rightKey = keys[rightChildPos]; if (Long.compare(rightKey, leftKey) * direction < 0) { smallestChildPos = rightChildPos; smallestChildKey = rightKey; } else { smallestChildPos = leftChildPos; smallestChildKey = leftKey; } } else { // There is no right child smallestChildPos = leftChildPos; smallestChildKey = leftKey; } if (Long.compare(objKey, smallestChildKey) * direction <= 0) break; heap[pos] = heap[smallestChildPos]; keys[pos] = smallestChildKey; pos = smallestChildPos; } return pos; } public void offer (T obj, long objKey) { if (rootMissing) { putBackRoot(obj, objKey); } else { offerInternal(obj, objKey); } //validateStructure(); } /* private void validateStructure() { for (int i = 2; i < mSize; i++) { long key = keys[i]; int parentPos = i >> 1; long parentKey = keys[parentPos]; if (Long.compare(parentKey, key) * direction > 0) { throw new AssertionError("Violated for key=" + i); } } } */ private void offerInternal (T obj, long objKey) { final int currentCapacity = heap.length; mSize++; if (mSize + 1 >= currentCapacity) { extendCapacity(currentCapacity); } /** * Put the element at new leaf location and adjust the heap. */ percUp (mSize, obj, objKey); } private void putBackRoot(T obj, long objKey) { rootMissing = false; percDown(1, obj, objKey); } @SuppressWarnings("unchecked") private void extendCapacity(int currentCapacity) { int newCapacity = currentCapacity * 2; Object[] newHeap = new Object[newCapacity]; System.arraycopy(heap, 0, newHeap, 0, mSize); this.heap = newHeap; long[] newKeyArray = new long[newCapacity]; System.arraycopy(keys, 0, newKeyArray, 0, mSize); this.keys = newKeyArray; } private int indexOfInternal (T obj) { for (int ii = 1; ii <= mSize; ii++) if (obj.equals (heap [ii])) return (ii); return (-1); } @SuppressWarnings ("unchecked") private T getInternal (int pos) { return (T) heap [pos]; } /* public MessageSource get (int idx) { return (MessageSource) (getInternal (idx + 1)); } */ private void removeInternal (int pos) { /** * We are removing element at pos, so we are going to have a hole there. * Put the last element in the hole and adjust the heap. */ // ByteArrayOutputStream buf = new ByteArrayOutputStream(); // dump(new PrintStream(buf)); T last = getInternal (mSize); long lastKey = keys[mSize]; heap [mSize] = null; mSize--; if (percDown(pos, last, lastKey) == pos) percUp(pos, last, lastKey); } /** * Similar to {@link #removeInternal} but for root position only (pos=0). */ private void removeRoot() { assert mSize > 0; // Take tha last tree element T last = getInternal(mSize); long lastKey = keys[mSize]; heap[mSize] = null; mSize--; // Push down that element from root position percDown(1, last, lastKey); } public boolean remove (T obj) { if (rootMissing) { removeRoot(); rootMissing = false; } int pos = indexOfInternal (obj); if (pos > 0) { removeInternal (pos); return (true); } return (false); } @Nullable public T poll () { if (isEmpty()) { return null; } if (rootMissing) { // Perform delayed root deletion removeRoot(); } rootMissing = true; return getInternal (1); } /** * @return returns current key of root. */ public long peekKey () { if (isEmpty()) { throw new IllegalStateException(); } if (rootMissing) { // Perform delayed root deletion removeRoot(); rootMissing = false; } return keys[1]; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy