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

org.apache.pulsar.common.util.collections.GrowablePriorityLongPairQueue Maven / Gradle / Ivy

There is a newer version: 4.0.0.4
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.pulsar.common.util.collections;

import static org.apache.pulsar.shade.com.google.common.base.Preconditions.checkArgument;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

/**
 * An unbounded priority queue based on a min heap where values are composed of pairs of longs.
 *
 * 

When the capacity is reached, data will be moved to a bigger array. * * It also act as a set and doesn't store duplicate values if {@link #allowedDuplicate} flag is passed false * *

(long,long) */ public class GrowablePriorityLongPairQueue { private long[] data; private int capacity; private static final AtomicIntegerFieldUpdater SIZE_UPDATER = AtomicIntegerFieldUpdater.newUpdater(GrowablePriorityLongPairQueue.class, "size"); private volatile int size = 0; private static final long EmptyItem = -1L; public GrowablePriorityLongPairQueue() { this(64); } public GrowablePriorityLongPairQueue(int initialCapacity) { checkArgument(initialCapacity > 0); this.capacity = io.netty.util.internal.MathUtil.findNextPositivePowerOfTwo(initialCapacity); data = new long[2 * capacity]; Arrays.fill(data, 0, data.length, EmptyItem); } /** * Predicate to checks for a key-value pair where both of them have long types. */ public interface LongPairPredicate { boolean test(long v1, long v2); } /** * Represents a function that accepts two long arguments. */ public interface LongPairConsumer { void accept(long v1, long v2); } public synchronized void add(long item1, long item2) { if (this.size >= this.capacity) { expandArray(); } int lastIndex = this.size << 1; data[lastIndex] = item1; data[lastIndex + 1] = item2; int loc = lastIndex; // Swap with parent until parent not larger while (loc > 0 && compare(loc, parent(loc)) < 0) { swap(loc, parent(loc)); loc = parent(loc); } SIZE_UPDATER.incrementAndGet(this); } public synchronized void forEach(LongPairConsumer processor) { int index = 0; for (int i = 0; i < this.size; i++) { processor.accept(data[index], data[index + 1]); index = index + 2; } } /** * @return a new list of all keys (makes a copy) */ public Set items() { Set items = new HashSet<>(this.size); forEach((item1, item2) -> items.add(new LongPair(item1, item2))); return items; } /** * @return a new list of keys with max provided numberOfItems (makes a copy) */ public Set items(int numberOfItems) { Set items = new HashSet<>(this.size); forEach((item1, item2) -> { if (items.size() < numberOfItems) { items.add(new LongPair(item1, item2)); } }); return items; } /** * Removes all of the elements of this collection that satisfy the given predicate. * * @param filter * a predicate which returns {@code true} for elements to be removed * * @return number of removed values */ public synchronized int removeIf(LongPairPredicate filter) { int removedValues = 0; int index = 0; long[] deletedItems = new long[size * 2]; int deleteItemsIndex = 0; // collect eligible items for deletion for (int i = 0; i < this.size; i++) { if (filter.test(data[index], data[index + 1])) { deletedItems[deleteItemsIndex++] = data[index]; deletedItems[deleteItemsIndex++] = data[index + 1]; removedValues++; } index = index + 2; } // delete collected items deleteItemsIndex = 0; for (int deleteItem = 0; deleteItem < removedValues; deleteItem++) { // delete item from the heap index = 0; for (int i = 0; i < this.size; i++) { if (data[index] == deletedItems[deleteItemsIndex] && data[index + 1] == deletedItems[deleteItemsIndex + 1]) { removeAtWithoutLock(index); } index = index + 2; } deleteItemsIndex = deleteItemsIndex + 2; } return removedValues; } /** * It removes all occurrence of given pair from the queue. * * @param item1 * @param item2 * @return */ public synchronized boolean remove(long item1, long item2) { boolean removed = false; int index = 0; for (int i = 0; i < this.size; i++) { if (data[index] == item1 && data[index + 1] == item2) { removeAtWithoutLock(index); removed = true; } index = index + 2; } return removed; } /** * Removes min element from the heap. * * @return */ public LongPair remove() { return removeAt(0); } private synchronized LongPair removeAt(int index) { return removeAtWithoutLock(index); } /** * it is not a thread-safe method and it should be called before acquiring a lock by a caller. * * @param index * @return */ private LongPair removeAtWithoutLock(int index) { if (this.size > 0) { LongPair item = new LongPair(data[index], data[index + 1]); data[index] = EmptyItem; data[index + 1] = EmptyItem; SIZE_UPDATER.decrementAndGet(this); int lastIndex = this.size << 1; swap(index, lastIndex); minHeapify(index, lastIndex - 2); return item; } else { return null; } } public synchronized LongPair peek() { if (this.size > 0) { return new LongPair(data[0], data[1]); } else { return null; } } public boolean isEmpty() { return this.size == 0; } public int capacity() { return this.capacity; } public synchronized void clear() { int index = 0; for (int i = 0; i < this.size; i++) { data[index] = -1; data[index + 1] = -1; index = index + 2; } this.size = 0; } public int size() { return this.size; } public synchronized boolean exists(long item1, long item2) { int index = 0; for (int i = 0; i < this.size; i++) { if (data[index] == item1 && data[index + 1] == item2) { return true; } index = index + 2; } return false; } private int compare(int index1, int index2) { if (data[index1] != data[index2]) { return Long.compare(data[index1], data[index2]); } else { return Long.compare(data[index1 + 1], data[index2 + 1]); } } private void expandArray() { this.capacity = capacity * 2; long[] newData = new long[2 * this.capacity]; int index = 0; for (int i = 0; i < this.size; i++) { newData[index] = data[index]; newData[index + 1] = data[index + 1]; index = index + 2; } Arrays.fill(newData, index, newData.length, EmptyItem); data = newData; } private void swap(int i, int j) { long t = data[i]; data[i] = data[j]; data[j] = t; t = data[i + 1]; data[i + 1] = data[j + 1]; data[j + 1] = t; } private static int leftChild(int i) { return (i << 1) + 2; } private static int rightChild(int i) { return (i << 1) + 4; } private static int parent(int i) { return ((i - 2) >> 1) & ~1; } private void minHeapify(int index, int lastIndex) { int left = leftChild(index); int right = rightChild(index); int smallest; if (left <= lastIndex && compare(left, index) < 0) { smallest = left; } else { smallest = index; } if (right <= lastIndex && compare(right, smallest) < 0) { smallest = right; } if (smallest != index) { swap(index, smallest); minHeapify(smallest, lastIndex); } } private static final long HashMixer = 0xc6a4a7935bd1e995L; private static final int R = 47; static final long hash(long key1, long key2) { long hash = key1 * HashMixer; hash ^= hash >>> R; hash *= HashMixer; hash += 31 + (key2 * HashMixer); hash ^= hash >>> R; hash *= HashMixer; return hash; } /** * Class representing two long values. */ public static class LongPair implements Comparable { public final long first; public final long second; public LongPair(long first, long second) { this.first = first; this.second = second; } @Override public boolean equals(Object obj) { if (obj instanceof LongPair) { LongPair other = (LongPair) obj; return first == other.first && second == other.second; } return false; } @Override public int hashCode() { return (int) hash(first, second); } @Override public int compareTo(LongPair o) { if (first != o.first) { return Long.compare(first, o.first); } else { return Long.compare(second, o.second); } } @Override public String toString() { return "LongPair [first=" + first + ", second=" + second + "]"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy