/* * 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. * * Contributions from 2013-2017 where performed either by US government * employees, or under US Veterans Health Administration contracts. * * US Veterans Health Administration contributions by government employees * are work of the U.S. Government and are not subject to copyright * protection in the United States. Portions contributed by government * employees are USGovWork (17USC ยง105). Not subject to copyright. * * Contribution by contractors to the US Veterans Health Administration * during this period are contractually contributed under the * Apache License, Version 2.0. * * See: https://www.usa.gov/government-works * * Contributions prior to 2013: * * Copyright (C) International Health Terminology Standards Development Organisation. * Licensed under the Apache License, Version 2.0. * */ package sh.isaac.api.collections.uuidnidmap; //~--- JDK imports ------------------------------------------------------------ import java.util.UUID; //~--- non-JDK imports -------------------------------------------------------- import org.apache.mahout.math.list.IntArrayList; import org.apache.mahout.math.set.AbstractSet; import sh.isaac.api.util.UUIDUtil; //~--- classes ---------------------------------------------------------------- /** * The Class AbstractUuidToIntHashMap. * * @author kec */ public abstract class AbstractUuidToIntHashMap extends AbstractSet { /** The Constant serialVersionUID. */ private static final long serialVersionUID = 1L; //~--- constructors -------------------------------------------------------- // public static int hashCollisions = 0; // for debug only /** * Makes this class non instantiable, but still let's others inherit from it. */ protected AbstractUuidToIntHashMap() {} //~--- methods ------------------------------------------------------------- /** * Returns {@code true} if the receiver contains the specified key. * * @param key the key * @return {@code true} if the receiver contains the specified key. */ public boolean containsKey(final long[] key) { return !forEachKey(iterKey -> ((key[0] != iterKey[0]) || (key[1] != iterKey[1]))); } /** * Returns a deep copy of the receiver; uses * {@code clone()} and casts the result. * * @return a deep copy of the receiver. * @throws CloneNotSupportedException the clone not supported exception */ public AbstractUuidToIntHashMap copy() throws CloneNotSupportedException { return (AbstractUuidToIntHashMap) clone(); } /** * Applies a procedure to each key of the receiver, if any. Note: Iterates over the keys in no particular * order. Subclasses can define a particular order, for example, "sorted by key". All methods which * <i>can</i> be expressed in terms of this method (most methods can) <i>must guarantee</i> to use the * <i>same</i> order defined by this method, even if it is no particular order. This is necessary so that, * for example, methods {@code keys} and {@code values} will yield association pairs, not two * uncorrelated lists. * * @param procedure the procedure to be applied. Stops iteration if the procedure returns {@code false}, * otherwise continues. * @return {@code false} if the procedure stopped before all keys where iterated over, {@code true} * otherwise. */ public abstract boolean forEachKey(UuidProcedure procedure); /** * Applies a procedure to each (key,value) pair of the receiver, if any. Iteration order is guaranteed to * be <i>identical</i> to the order used by method {@link #forEachKey(UuidProcedure)}. * * @param procedure the procedure to be applied. Stops iteration if the procedure returns {@code false}, * otherwise continues. * @return {@code false} if the procedure stopped before all keys where iterated over, {@code true} * otherwise. */ public boolean forEachPair(final UuidIntProcedure procedure) { return forEachKey(key -> procedure.apply(key, get(key))); } /** * Returns the first key the given value is associated with. It is often a good idea to first check with * {@link #containsValue(int)} whether there exists an association from a key to this value. Search order * is guaranteed to be <i>identical</i> to the order used by method {@link #forEachKey(UuidProcedure)}. * * @param value the value to search for. * @return the first key for which holds {@code get(key) == value}; returns {@code Double.NaN} if no * such key exists. */ public long[] keyOf(final int value) { final long[] foundKey = new long[2]; final boolean notFound = forEachPair((long[] iterKey, int iterValue) -> { final boolean found = value == iterValue; if (found) { foundKey[0] = iterKey[0]; foundKey[1] = iterKey[1]; } return !found; }); if (notFound) { return null; } return foundKey; } /** * Returns a list filled with all keys contained in the receiver. The returned list has a size that equals * {@code this.size()}. Note: Keys are filled into the list in no particular order. However, the order is * <i>identical</i> to the order used by method {@link #forEachKey(UuidProcedure)}. <p> This method can be * used to iterate over the keys of the receiver. * * @return the keys. */ public UuidArrayList keys() { final UuidArrayList list = new UuidArrayList(size()); keys(list); return list; } /** * Fills all keys contained in the receiver into the specified list. Fills the list, starting at index 0. * After this call returns the specified list has a new size that equals {@code this.size()}. Iteration * order is guaranteed to be <i>identical</i> to the order used by method * {@link #forEachKey(UuidProcedure)}. <p> This method can be used to iterate over the keys of the * receiver. * * @param list the list to be filled, can have any size. */ public void keys(final UuidArrayList list) { list.clear(); forEachKey(key -> { list.add(key); return true; }); } /** * Fills all pairs satisfying a given condition into the specified lists. Fills into the lists, starting * at index 0. After this call returns the specified lists both have a new size, the number of pairs * satisfying the condition. Iteration order is guaranteed to be <i>identical</i> to the order used by * method {@link #forEachKey(UuidProcedure)}. <p> <b>Example:</b> <br> * * <pre> * UuidIntProcedure condition = new UuidIntProcedure() { // match even values only * public boolean apply(double key, int value) { return value%2==0; } * } * keys = (8,7,6), values = (1,2,2) --> keyList = (6,8), valueList = (2,1)} * </pre> * * @param condition the condition to be matched. Takes the current key as first and the current value as * second argument. * @param keyList the list to be filled with keys, can have any size. * @param valueList the list to be filled with values, can have any size. */ public void pairsMatching(final UuidIntProcedure condition, final UuidArrayList keyList, final IntArrayList valueList) { keyList.clear(); valueList.clear(); forEachPair((long[] key, int value) -> { if (condition.apply(key, value)) { keyList.add(key); valueList.add(value); } return true; }); } /** * Fills all keys and values <i>sorted ascending by key</i> into the specified lists. Fills into the * lists, starting at index 0. After this call returns the specified lists both have a new size that * equals {@code this.size()}. <p> <b>Example:</b> <br> {@code keys = (8,7,6), values = (1,2,2) --> keyList * = (6,7,8), valueList = (2,2,1)} * * @param keyList the list to be filled with keys, can have any size. * @param valueList the list to be filled with values, can have any size. */ public void pairsSortedByKey(final UuidArrayList keyList, final IntArrayList valueList) { /* * keys(keyList); values(valueList); * * final double[] k = keyList.elements(); final int[] v = * valueList.elements(); org.ihtsdo.Swapper swapper = new * org.ihtsdo.Swapper() { public void swap(int a, int b) { int t1; double * t2; t1 = v[a]; v[a] = v[b]; v[b] = t1; t2 = k[a]; k[a] = k[b]; k[b] = * t2; } }; * * org.ihtsdo.function.IntComparator comp = new * org.ihtsdo.function.IntComparator() { public int compare(int a, int b) * { return k[a]<k[b] ? -1 : k[a]==k[b] ? 0 : 1; } }; * org.ihtsdo.MultiSorting.sort(0,keyList.size(),comp,swapper); */ // this variant may be quicker // org.ihtsdo.map.OpenDoubleIntHashMap.hashCollisions = 0; // System.out.println("collisions="+org.ihtsdo.map.OpenDoubleIntHashMap.hashCollisions); keys(keyList); keyList.sort(); valueList.setSize(keyList.size()); for (int i = keyList.size(); --i >= 0; ) { valueList.setQuick(i, get(keyList.getQuick(i))); } // System.out.println("collisions="+org.ihtsdo.map.OpenDoubleIntHashMap.hashCollisions); } /** * Associates the given key with the given value. Replaces any old {@code (key,someOtherValue)} * association, if existing. * * @param key the key the value shall be associated with. * @param value the value to be associated. * @return {@code true} if the receiver did not already contain such a key; {@code false} if the * receiver did already contain such a key - the new value has now replaced the formerly associated value. */ public abstract boolean put(long[] key, int value); /** * Put. * * @param key the key * @param value the value * @return true, if successful */ public boolean put(UUID key, int value) { return put(UUIDUtil.convert(key), value); } /** * Removes the given key with its associated element from the receiver, if present. * * @param key the key to be removed from the receiver. * @return {@code true} if the receiver contained the specified key, {@code false} otherwise. */ public abstract boolean removeKey(long[] key); /** * Returns a string representation of the receiver, containing the String representation of each key-value * pair, sorted ascending by key. * * @return the string */ @Override public String toString() { final UuidArrayList theKeys = keys(); theKeys.sort(); final StringBuilder buf = new StringBuilder(); buf.append("["); final int maxIndex = theKeys.size() - 1; for (int i = 0; i <= maxIndex; i++) { final long[] key = theKeys.get(i); final UUID uuidKey = new UUID(key[0], key[1]); buf.append(uuidKey.toString()); buf.append("->"); buf.append(String.valueOf(get(key))); if (i < maxIndex) { buf.append(", "); } } buf.append("]"); return buf.toString(); } /** * Returns a list filled with all values contained in the receiver. The returned list has a size that * equals {@code this.size()}. Iteration order is guaranteed to be <i>identical</i> to the order used by * method {@link #forEachKey(UuidProcedure)}. <p> This method can be used to iterate over the values of * the receiver. * * @return the values. */ public IntArrayList values() { final IntArrayList list = new IntArrayList(size()); values(list); return list; } /** * Fills all values contained in the receiver into the specified list. Fills the list, starting at index * 0. After this call returns the specified list has a new size that equals {@code this.size()}. * Iteration order is guaranteed to be <i>identical</i> to the order used by method * {@link #forEachKey(UuidProcedure)}. <p> This method can be used to iterate over the values of the * receiver. * * @param list the list to be filled, can have any size. */ public void values(final IntArrayList list) { list.clear(); forEachKey(key -> { list.add(get(key)); return true; }); } //~--- get methods --------------------------------------------------------- /** * Returns the value associated with the specified key. It is often a good idea to first check with * {@link #containsKey(double)} whether the given key has a value associated or not, i.e. whether there * exists an association for the given key or not. * * @param key the key to be searched for. * @return the value associated with the specified key; {@code 0} if no such key is present. */ public abstract int get(long[] key); }