2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.android;
24 * Copyright (C) 2006 The Android Open Source Project
26 * Licensed under the Apache License, Version 2.0 (the "License");
27 * you may not use this file except in compliance with the License.
28 * You may obtain a copy of the License at
30 * http://www.apache.org/licenses/LICENSE-2.0
32 * Unless required by applicable law or agreed to in writing, software
33 * distributed under the License is distributed on an "AS IS" BASIS,
34 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
35 * See the License for the specific language governing permissions and
36 * limitations under the License.
39 * SparseDoubleArray map integers to doubles. Unlike a normal array of integers,
40 * there can be gaps in the indices. It is intended to be more memory efficient
41 * than using a HashMap to map Integer to Double, both because it avoids
42 * auto-boxing keys and values and its data structure doesn't rely on an extra
43 * entry object for each mapping.
46 * Note that this container keeps its mappings in an array data structure, using
47 * a binary search to find keys. The implementation is not intended to be
48 * appropriate for data structures that may contain large numbers of items. It
49 * is generally slower than a traditional HashMap, since lookups require a
50 * binary search and adds and removes require inserting and deleting entries in
51 * the array. For containers holding up to hundreds of items, the performance
52 * difference is not significant, less than 50%.
56 * It is possible to iterate over the items in this container using
57 * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
58 * <code>keyAt(int)</code> with ascending values of the index will return the
59 * keys in ascending order, or the values corresponding to the keys in ascending
60 * order in the case of <code>valueAt(int)<code>.
66 * Jan 2017 cloned from SparseIntArray for Jalview to support SparseMatrix
67 * - SparseDoubleArray(double[]) constructor added
68 * - multiply() added for more efficient multiply (or divide) of a value
70 public class SparseDoubleArray implements Cloneable
74 private double[] mValues;
79 * Creates a new SparseDoubleArray containing no mappings.
81 public SparseDoubleArray()
87 * Creates a new SparseDoubleArray containing no mappings that will not
88 * require any additional memory allocation to store the specified number of
89 * mappings. If you supply an initial capacity of 0, the sparse array will be
90 * initialized with a light-weight representation not requiring any additional
93 public SparseDoubleArray(int initialCapacity)
95 if (initialCapacity == 0)
97 mKeys = ContainerHelpers.EMPTY_INTS;
98 mValues = ContainerHelpers.EMPTY_DOUBLES;
102 initialCapacity = idealDoubleArraySize(initialCapacity);
103 mKeys = new int[initialCapacity];
104 mValues = new double[initialCapacity];
110 * Constructor given an array of double values; stores the non-zero values
114 public SparseDoubleArray(double[] row)
117 for (int i = 0; i < row.length; i++)
127 public SparseDoubleArray clone()
129 SparseDoubleArray clone = null;
132 clone = (SparseDoubleArray) super.clone();
133 clone.mKeys = mKeys.clone();
134 clone.mValues = mValues.clone();
135 } catch (CloneNotSupportedException cnse)
143 * Gets the value mapped from the specified key, or <code>0</code> if no such
144 * mapping has been made.
146 public double get(int key)
152 * Gets the int mapped from the specified key, or the specified value if no
153 * such mapping has been made.
155 public double get(int key, double valueIfKeyNotFound)
157 int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
160 return valueIfKeyNotFound;
169 * Removes the mapping from the specified key, if there was any.
171 public void delete(int key)
173 int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
181 * Removes the mapping at the given index.
183 public void removeAt(int index)
185 System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
186 System.arraycopy(mValues, index + 1, mValues, index,
187 mSize - (index + 1));
192 * Adds a mapping from the specified key to the specified value, replacing the
193 * previous mapping from the specified key if there was one.
195 public void put(int key, double value)
197 int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
205 if (mSize >= mKeys.length)
207 int n = idealDoubleArraySize(mSize + 1);
208 int[] nkeys = new int[n];
209 double[] nvalues = new double[n];
210 // Log.e("SparseDoubleArray", "grow " + mKeys.length + " to " + n);
211 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
212 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
218 // Log.e("SparseDoubleArray", "move " + (mSize - i));
219 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
220 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
229 * Returns the number of key-value mappings that this SparseDoubleArray
238 * Given an index in the range <code>0...size()-1</code>, returns the key from
239 * the <code>index</code>th key-value mapping that this SparseDoubleArray
243 * The keys corresponding to indices in ascending order are guaranteed to be
244 * in ascending order, e.g., <code>keyAt(0)</code> will return the smallest
245 * key and <code>keyAt(size()-1)</code> will return the largest key.
248 public int keyAt(int index)
254 * Given an index in the range <code>0...size()-1</code>, returns the value
255 * from the <code>index</code>th key-value mapping that this SparseDoubleArray
259 * The values corresponding to indices in ascending order are guaranteed to be
260 * associated with keys in ascending order, e.g., <code>valueAt(0)</code> will
261 * return the value associated with the smallest key and
262 * <code>valueAt(size()-1)</code> will return the value associated with the
266 public double valueAt(int index)
268 return mValues[index];
272 * Returns the index for which {@link #keyAt} would return the specified key,
273 * or a negative number if the specified key is not mapped.
275 public int indexOfKey(int key)
277 return ContainerHelpers.binarySearch(mKeys, mSize, key);
281 * Returns an index for which {@link #valueAt} would return the specified key,
282 * or a negative number if no keys map to the specified value. Beware that
283 * this is a linear search, unlike lookups by key, and that multiple keys can
284 * map to the same value and this will find only one of them.
286 public int indexOfValue(double value)
288 for (int i = 0; i < mSize; i++)
290 if (mValues[i] == value)
299 * Removes all key-value mappings from this SparseDoubleArray.
307 * Puts a key/value pair into the array, optimizing for the case where the key
308 * is greater than all existing keys in the array.
310 public void append(int key, double value)
312 if (mSize != 0 && key <= mKeys[mSize - 1])
318 if (pos >= mKeys.length)
320 int n = idealDoubleArraySize(pos + 1);
321 int[] nkeys = new int[n];
322 double[] nvalues = new double[n];
323 // Log.e("SparseDoubleArray", "grow " + mKeys.length + " to " + n);
324 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
325 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
330 mValues[pos] = value;
335 * Created by analogy with
336 * com.android.internal.util.ArrayUtils#idealLongArraySize
341 public static int idealDoubleArraySize(int need)
343 return idealByteArraySize(need * 8) / 8;
347 * Inlined here by copying from com.android.internal.util.ArrayUtils
352 public static int idealByteArraySize(int need)
354 for (int i = 4; i < 32; i++)
356 if (need <= (1 << i) - 12)
358 return (1 << i) - 12;
369 * This implementation composes a string by iterating over its mappings.
372 public String toString()
378 StringBuilder buffer = new StringBuilder(mSize * 28);
380 for (int i = 0; i < mSize; i++)
389 double value = valueAt(i);
390 buffer.append(value);
393 return buffer.toString();
397 * Method (copied from put) added for Jalview to efficiently increment a key's
398 * value if present, else add it with the given value. This avoids a double
399 * binary search (once to get the value, again to put the updated value).
403 * @return the new value for the key
405 public double add(int key, double toAdd)
407 double newValue = toAdd;
408 int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
412 newValue = mValues[i];
417 if (mSize >= mKeys.length)
419 int n = idealDoubleArraySize(mSize + 1);
420 int[] nkeys = new int[n];
421 double[] nvalues = new double[n];
422 System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
423 System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
429 System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
430 System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
440 * Method added for Jalview to efficiently multiply a key's value if present,
441 * else do nothing. This avoids a double binary search (once to get the value,
442 * again to put the updated value).
446 * @return the new value for the key
448 public double divide(int key, double divisor)
450 double newValue = 0d;
455 int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
458 mValues[i] /= divisor;
459 newValue = mValues[i];