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

com.swirlds.fchashmap.FCInvertibleHashMap Maven / Gradle / Ivy

Go to download

Swirlds is a software platform designed to build fully-distributed applications that harness the power of the cloud without servers. Now you can develop applications with fairness in decision making, speed, trust and reliability, at a fraction of the cost of traditional server-based platforms.

There is a newer version: 0.56.5
Show newest version
/*
 * Copyright (C) 2021-2024 Hedera Hashgraph, LLC
 *
 * 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.swirlds.fchashmap;

import com.swirlds.common.FastCopyable;
import java.util.AbstractMap;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/**
 * A fast copyable hashmap that is also able to generate inverse mappings between a value and the set of all
 * keys that map to that value. Supports pagination.
 * 

* This data structure does not support null values. * * @param * the type of the key. This key must be effectively immutable -- that is, no changes to the key after its * insertion may have any effect on its {@link Object#equals(Object)} or {@link Object#hashCode()}. * @param * the type of the value. This key must be effectively immutable -- that is, no changes to the value after its * insertion may have any effect on its {@link Object#equals(Object)} or {@link Object#hashCode()}. */ public class FCInvertibleHashMap extends AbstractMap implements FastCopyable { private final FCHashMap map; private final FCOneToManyRelation inverseRelation; private boolean immutable; /** * Create a new {@link FCInvertibleHashMap}. */ public FCInvertibleHashMap() { this(0); } /** * Create a new {@link FCInvertibleHashMap} with an initial capacity. * * @param capacity * the initial capacity of the hashmap */ public FCInvertibleHashMap(final int capacity) { this.map = new FCHashMap<>(capacity); this.inverseRelation = new FCOneToManyRelation<>(); this.immutable = false; } /** * Copy constructor. * * @param that * the map to copy */ private FCInvertibleHashMap(final FCInvertibleHashMap that) { this.map = that.map.copy(); this.inverseRelation = that.inverseRelation.copy(); that.immutable = true; this.immutable = false; } /** * {@inheritDoc} */ @Override public FCInvertibleHashMap copy() { return new FCInvertibleHashMap<>(this); } /** * {@inheritDoc} */ @Override public boolean isImmutable() { return immutable; } /** * Utility function. Throw if the key is null. */ private void throwIfNullKey(final K key) { if (key == null) { throw new IllegalArgumentException("null keys are not supported"); } } /** * Utility function. Throw if the value is null. */ private void throwIfNullValue(final V value) { if (value == null) { throw new IllegalArgumentException("null values are not supported"); } } /** * Given a value, return an iterator that iterates over the set of keys that map to the value. *

* This iterator should not used after this copy of the map is updated in any way. * This iterator may exhibit undefined behavior if this copy of the map is updated between * the creation of this iterator and the use of this iterator. * * @param value * the value in question * @return an iterator for the key set */ public Iterator inverseGet(final V value) { return inverseRelation.get(value); } /** * Given a value, return an iterator that iterates over a paginated set of keys that map to the value. Iteration * starts at a given index and continues until the end of the key set. *

* This iterator should not used after this copy of the map is updated in any way. * This iterator may exhibit undefined behavior if this copy of the map is updated between * the creation of this iterator and the use of this iterator. * * @param value * the value in question * @param startIndex * the index of the first key to return (inclusive). * An index of 0 starts iteration at the first key in the set. * @return an iterator for the key set starting at the given index */ public Iterator inverseGet(final V value, final int startIndex) { return inverseRelation.get(value, startIndex); } /** * Given a value, return an iterator that iterates over a paginated set of keys that map to the value. *

* This iterator should not used after this copy of the map is updated in any way. * This iterator may exhibit undefined behavior if this copy of the map is updated between * the creation of this iterator and the use of this iterator. * * @param value * the value in question * @param startIndex * the index of the first key to return (inclusive). * An index of 0 starts iteration at the first key in the set. * @param endIndex * the index bounding the last key to return (exclusive). * An index of N will cause the set to include the last element if there are N elements. * @return an iterator for the key set starting at the given index */ public Iterator inverseGet(final V value, final int startIndex, final int endIndex) { return inverseRelation.get(value, startIndex, endIndex); } /** * Given a value, return a list that contains a list of keys that map to that value. * * @return an list containing the requested range */ public List inverseGetList(final V value) { return inverseRelation.getList(value); } /** * Given a value, return a list that contains a list of keys that start at a given index. * * @param startIndex * the index of the first key to return (inclusive). * An index of 0 starts at the first key in the set. * @return an list containing the requested range */ public List inverseGetList(final V value, final int startIndex) { return inverseRelation.getList(value, startIndex); } /** * Given a value, return a list that contains a paginated list of keys that map to the value. * * @param startIndex * the index of the first key to return (inclusive). * An index of 0 starts at the first key in the set. * @param endIndex * the index bounding the last key to return (exclusive). * An index of N will cause the set to include the last element if there are N elements. * @return an list containing the requested range */ public List inverseGetList(final V value, final int startIndex, final int endIndex) { return inverseRelation.getList(value, startIndex, endIndex); } /** * Return a count of the number of keys that reference a given value, i.e. the number of times that a value * appears in the map. * * @param value * the value in question * @return the number of keys that reference the value */ public int getValueCount(final V value) { throwIfNullValue(value); return inverseRelation.getCount(value); } /** * {@inheritDoc} */ @Override public synchronized boolean release() { map.release(); inverseRelation.release(); return true; } /** * {@inheritDoc} */ @Override public V put(final K key, final V value) { throwIfImmutable(); throwIfNullKey(key); throwIfNullValue(value); final V previousValue = map.get(key); if (Objects.equals(previousValue, value)) { return previousValue; } if (previousValue != null) { final boolean disassociated = inverseRelation.disassociate(previousValue, key); if (!disassociated) { // Sanity check, this should never happen throw new IllegalStateException("unsuccessful disassociation"); } } map.put(key, value); final boolean associated = inverseRelation.associate(value, key); if (!associated) { // Sanity check, this should never happen throw new IllegalStateException("unsuccessful association"); } return previousValue; } /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public V remove(final Object key) { throwIfImmutable(); throwIfNullKey((K) key); final V value = map.remove(key); if (value != null) { final boolean disassociated = inverseRelation.disassociate(value, (K) key); if (!disassociated) { // Sanity check, this should never happen throw new IllegalStateException("unsuccessful disassociation"); } } return value; } /** * {@inheritDoc} */ @Override public Set> entrySet() { return map.entrySet(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy