com.hivemq.util.IntMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hivemq-community-edition-embedded Show documentation
Show all versions of hivemq-community-edition-embedded Show documentation
HiveMQ CE is a Java-based open source MQTT broker that fully supports MQTT 3.x and MQTT 5
The newest version!
/*
* Copyright 2019-present HiveMQ GmbH
*
* 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.hivemq.util;
import com.hivemq.extension.sdk.api.annotations.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import java.util.Arrays;
import java.util.Iterator;
/**
* This int to int map designed for minimal memory overhead for instaces with very few entries.
* Access is to the map is linear at best, therefore this implementation should not be used if cpu time is a concern.
* This class is not thread save.
* Each instance of this IntMap must not be accessed by more than one thread at a time.
*
* @author Lukas Brandl
*/
@NotThreadSafe
public class IntMap implements Iterable {
private int[] backingArray = null;
public IntMap() {
}
/**
* Fill the array with keys from 0 to initialSize mapped to 0.
* This is intended to avoid garbage when the array is growing.
*
* @param initialSize of the backing array
*/
public IntMap(final int initialSize) {
backingArray = new int[initialSize * 2];
for (int i = 0; i < backingArray.length; i += 2) {
backingArray[i] = i;
}
}
/**
* Creates a copy of a given IntMap instance
*
* @param intMap The instance to be copied
*/
public IntMap(final IntMap intMap) {
if (intMap.backingArray == null) {
this.backingArray = null;
} else {
this.backingArray = Arrays.copyOf(intMap.backingArray, intMap.backingArray.length);
}
}
/**
* @param key is associated with a value
* @return the value associated with the key or zero
*/
public int get(final int key) {
if (backingArray == null) {
return 0;
}
for (int i = 0; i < backingArray.length; i += 2) {
if (backingArray[i] == key) {
return backingArray[i + 1];
}
}
return 0;
}
/**
* @param key is associated with a value
* @param value will be accessible by the key
* @return the previous value
*/
public int put(final int key, final int value) {
// zero is default, so there is no need to put it
if (value == 0) {
return remove(key);
}
//shortcut
if (backingArray == null) {
backingArray = new int[2];
backingArray[0] = key;
backingArray[1] = value;
return 0;
}
for (int i = 0; i < backingArray.length; i += 2) {
if (backingArray[i] == key) {
final int previousValue = backingArray[i + 1];
backingArray[i + 1] = value;
return previousValue;
}
}
growArray();
backingArray[backingArray.length - 2] = key;
backingArray[backingArray.length - 1] = value;
return 0;
}
/**
* @param key for which the value is incremented
* @return the previous value
*/
public int increment(final int key) {
// Zero is the default, therefore values of zero are not present
// I case the key that should be incremented is not present it is considered to be zero
// Therefore we crate a new entry with value 1
if (backingArray == null) {
backingArray = new int[2];
backingArray[0] = key;
backingArray[1] = 1;
return 0;
}
for (int i = 0; i < backingArray.length; i += 2) {
if (backingArray[i] == key) {
final int previousValue = backingArray[i + 1];
backingArray[i + 1] = backingArray[i + 1] + 1;
return previousValue;
}
}
put(key, 1);
return 0;
}
/**
* This method will overwrite the current value at the given index
* WARNING: It will not check if the array is big enough or even initialised
*
* @param key is associated with a value
* @param value will be accessible by the key
* @param index the index at which the entry is put
*/
public void putUnsafe(final int key, final int value, final int index) {
final int keyIndex = index * 2;
backingArray[keyIndex] = key;
backingArray[keyIndex + 1] = value;
}
/**
* Sets a value for a given key to zero
*
* @param key to be removed
* @return the removed value
*/
public int remove(final int key) {
if (backingArray == null) {
return 0;
}
for (int i = 0; i < backingArray.length; i += 2) {
if (backingArray[i] == key) {
backingArray[i] = backingArray[backingArray.length - 2];
final int previousValue = backingArray[i + 1];
backingArray[i + 1] = backingArray[backingArray.length - 1];
shrinkArray();
return previousValue;
}
}
return 0;
}
/**
* Set all values of an other IntMap to the current array, if the value is bigger than the current one
*
* @param intMap to be merged
*/
public void mergeMax(final IntMap intMap) {
final int[] otherArray = intMap.backingArray;
if (otherArray == null) {
return;
}
for (int i = 0; i < otherArray.length; i += 2) {
final int key = otherArray[i];
final int currentValue = this.get(key);
final int otherValue = otherArray[i + 1];
if (otherValue > currentValue) {
put(key, otherValue);
}
}
}
/**
* @return the size of the map
*/
public int size() {
if (backingArray == null) {
return 0;
}
return backingArray.length / 2;
}
public boolean isEmpty() {
if (backingArray == null) {
return true;
}
return backingArray.length == 0;
}
private void shrinkArray() {
if (backingArray == null || backingArray.length == 0) {
return;
}
final int[] newArray = new int[backingArray.length - 2];
System.arraycopy(backingArray, 0, newArray, 0, newArray.length);
backingArray = newArray;
}
private void growArray() {
final int[] newArray = new int[backingArray.length + 2];
System.arraycopy(backingArray, 0, newArray, 0, backingArray.length);
backingArray = newArray;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final IntMap that = (IntMap) o;
return Arrays.equals(backingArray, that.backingArray);
}
@Override
public int hashCode() {
return Arrays.hashCode(backingArray);
}
@Override
public Iterator iterator() {
return new IntMapIterator(this);
}
private static class IntMapIterator implements Iterator {
private final IntMap map;
private int nextIndex = 0;
private IntMapIterator(final IntMap map) {
this.map = map;
}
@Override
public boolean hasNext() {
if (map.backingArray == null) {
return false;
}
return nextIndex < map.backingArray.length;
}
@Override
public IntMapEntry next() {
if (!hasNext()) {
return null;
}
final IntMapEntry entry = new IntMapEntry(map.backingArray[nextIndex], map.backingArray[nextIndex + 1]);
nextIndex += 2;
return entry;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
@Immutable
public static class IntMapEntry {
private final int key;
private final int value;
private IntMapEntry(final int key, final int value) {
this.key = key;
this.value = value;
}
public int getKey() {
return key;
}
public int getValue() {
return value;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final IntMapEntry that = (IntMapEntry) o;
if (key != that.key) {
return false;
}
return value == that.value;
}
@Override
public int hashCode() {
int result = key;
result = 31 * result + value;
return result;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy