X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;ds=sidebyside;f=src%2Fjalview%2Futil%2FSparseCount.java;fp=src%2Fjalview%2Futil%2FSparseCount.java;h=e6b45f205af5bdc429540c29594489257fab2982;hb=fc9aecdad8fb4d6ac38e504b094b29cf2223b32b;hp=0000000000000000000000000000000000000000;hpb=08a75320d4e492edb4f308d0ab6c1044896dfd96;p=jalview.git diff --git a/src/jalview/util/SparseCount.java b/src/jalview/util/SparseCount.java new file mode 100644 index 0000000..e6b45f2 --- /dev/null +++ b/src/jalview/util/SparseCount.java @@ -0,0 +1,154 @@ +package jalview.util; + +import jalview.ext.android.SparseIntArray; +import jalview.ext.android.SparseShortArray; + +/** + * A class to count occurrences of characters with minimal memory footprint. + * Sparse arrays of short values are used to hold the counts, with automatic + * promotion to arrays of int if any count exceeds the maximum value for a + * short. + * + * @author gmcarstairs + * + */ +public class SparseCount +{ + private static final int DEFAULT_PROFILE_SIZE = 2; + + /* + * array of keys (chars) and values (counts) + * held either as shorts or (if shorts overflow) as ints + */ + private SparseShortArray shortProfile; + + private SparseIntArray intProfile; + + /* + * flag is set true after short overflow occurs + */ + private boolean useInts; + + /** + * Constructor which initially creates a new sparse array of short values to + * hold counts. + * + * @param profileSize + */ + public SparseCount(int profileSize) + { + this.shortProfile = new SparseShortArray(profileSize); + } + + /** + * Constructor which allocates an initial count array for only two distinct + * values (the array will grow if needed) + */ + public SparseCount() + { + this(DEFAULT_PROFILE_SIZE); + } + + /** + * Adds the given value for the given key (or sets the initial value), and + * returns the new value + * + * @param key + * @param value + */ + public int add(int key, int value) + { + int newValue = 0; + if (useInts) + { + newValue = intProfile.add(key, value); + } + else + { + try { + newValue = shortProfile.add(key, value); + } catch (ArithmeticException e) { + handleOverflow(); + newValue = intProfile.add(key, value); + } + } + return newValue; + } + + /** + * Switch from counting shorts to counting ints + */ + synchronized void handleOverflow() + { + int size = shortProfile.size(); + intProfile = new SparseIntArray(size); + for (int i = 0; i < size; i++) + { + short key = shortProfile.keyAt(i); + short value = shortProfile.valueAt(i); + intProfile.put(key, value); + } + shortProfile = null; + useInts = true; + } + + /** + * Returns the size of the profile (number of distinct items counted) + * + * @return + */ + public int size() + { + return useInts ? intProfile.size() : shortProfile.size(); + } + + /** + * Returns the value for the key (zero if no such key) + * + * @param key + * @return + */ + public int get(int key) + { + return useInts ? intProfile.get(key) : shortProfile.get(key); + } + + /** + * Sets the value for the given key + * + * @param key + * @param value + */ + public void put(int key, int value) + { + if (useInts) + { + intProfile.put(key, value); + } + else + { + shortProfile.put(key, value); + } + } + + public int keyAt(int k) + { + return useInts ? intProfile.keyAt(k) : shortProfile.keyAt(k); + } + + public int valueAt(int k) + { + return useInts ? intProfile.valueAt(k) : shortProfile.valueAt(k); + } + + /** + * Answers true if this object wraps arrays of int values, false if using + * short values + * + * @return + */ + boolean isUsingInt() + { + return useInts; + } +}