several fixes, cleanups, and speed improvements to dictionary classes

This commit is contained in:
Ben Fry
2016-04-11 17:03:59 -04:00
parent 5f2e6b2b1b
commit 9c5579b233
4 changed files with 244 additions and 345 deletions

View File

@@ -69,6 +69,22 @@ public class FloatDict {
}
/**
* @nowebref
*/
public FloatDict(String[] keys, float[] values) {
if (keys.length != values.length) {
throw new IllegalArgumentException("key and value arrays must be the same length");
}
this.keys = keys;
this.values = values;
count = keys.length;
for (int i = 0; i < count; i++) {
indices.put(keys[i], i);
}
}
/**
* Constructor to allow (more intuitive) inline initialization, e.g.:
* <pre>
@@ -90,21 +106,6 @@ public class FloatDict {
}
/**
* @nowebref
*/
public FloatDict(String[] keys, float[] values) {
if (keys.length != values.length) {
throw new IllegalArgumentException("key and value arrays must be the same length");
}
this.keys = keys;
this.values = values;
count = keys.length;
for (int i = 0; i < count; i++) {
indices.put(keys[i], i);
}
}
/**
* @webref floatdict:method
* @brief Returns the number of key/value pairs
@@ -139,71 +140,35 @@ public class FloatDict {
}
// /**
// * Return the internal array being used to store the keys. Allocated but
// * unused entries will be removed. This array should not be modified.
// */
// public String[] keys() {
// crop();
// return keys;
// }
/**
* @webref floatdict:method
* @brief Return the internal array being used to store the keys
*/
public Iterable<String> keys() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
int index = -1;
public void remove() {
removeIndex(index);
}
public String next() {
return key(++index);
}
public boolean hasNext() {
return index+1 < size();
}
};
return keyIterator();
}
};
}
// Use this to iterate when you want to be able to remove elements along the way
public Iterator<String> keyIterator() {
return new Iterator<String>() {
int index = -1;
public void remove() {
removeIndex(index);
}
/*
static class KeyIterator implements Iterator<String> {
FloatHash parent;
int index;
public String next() {
return key(++index);
}
public KeyIterator(FloatHash parent) {
this.parent = parent;
index = -1;
}
public void remove() {
parent.removeIndex(index);
}
public String next() {
return parent.key(++index);
}
public boolean hasNext() {
return index+1 < parent.size();
}
public void reset() {
index = -1;
}
public boolean hasNext() {
return index+1 < size();
}
};
}
*/
/**
@@ -213,6 +178,7 @@ public class FloatDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
crop();
return keyArray(null);
}
@@ -231,11 +197,6 @@ public class FloatDict {
}
// public float[] values() {
// crop();
// return values;
// }
/**
* @webref floatdict:method
* @brief Return the internal array being used to store the values
@@ -245,21 +206,26 @@ public class FloatDict {
@Override
public Iterator<Float> iterator() {
return new Iterator<Float>() {
int index = -1;
return valueIterator();
}
};
}
public Iterator<Float> valueIterator() {
return new Iterator<Float>() {
int index = -1;
public void remove() {
removeIndex(index);
}
public void remove() {
removeIndex(index);
}
public Float next() {
return value(++index);
}
public Float next() {
return value(++index);
}
public boolean hasNext() {
return index+1 < size();
}
};
public boolean hasNext() {
return index+1 < size();
}
};
}
@@ -272,6 +238,7 @@ public class FloatDict {
* @brief Create a new array and copy each of the values into it
*/
public float[] valueArray() {
crop();
return valueArray(null);
}
@@ -337,18 +304,6 @@ public class FloatDict {
}
// /** Increase the value of a specific key by 1. */
// public void inc(String key) {
// inc(key, 1);
//// int index = index(key);
//// if (index == -1) {
//// create(key, 1);
//// } else {
//// values[index]++;
//// }
// }
/**
* @webref floatdict:method
* @brief Add to a value
@@ -363,12 +318,6 @@ public class FloatDict {
}
// /** Decrease the value of a key by 1. */
// public void dec(String key) {
// inc(key, -1);
// }
/**
* @webref floatdict:method
* @brief Subtract from a value
@@ -417,8 +366,10 @@ public class FloatDict {
* @brief Return the smallest value
*/
public int minIndex() {
checkMinMax("minIndex");
// Will still return NaN if there is 1 or more entries, and they're all NaN
//checkMinMax("minIndex");
if (count == 0) return -1;
// Will still return NaN if there are 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
for (int i = 0; i < count; i++) {
@@ -430,7 +381,7 @@ public class FloatDict {
// calculate the rest
for (int j = i+1; j < count; j++) {
float d = values[j];
if (!Float.isNaN(d) && (d < m)) {
if ((d == d) && (d < m)) {
m = values[j];
mi = j;
}
@@ -442,6 +393,7 @@ public class FloatDict {
}
// return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
int index = minIndex();
@@ -452,6 +404,7 @@ public class FloatDict {
}
// return the minimum value, or throw an error if there are no values
public float minValue() {
checkMinMax("minValue");
int index = minIndex();
@@ -468,7 +421,10 @@ public class FloatDict {
*/
// The index of the entry that has the max value. Reference above is incorrect.
public int maxIndex() {
checkMinMax("maxIndex");
//checkMinMax("maxIndex");
if (count == 0) {
return -1;
}
// Will still return NaN if there is 1 or more entries, and they're all NaN
float m = Float.NaN;
int mi = -1;
@@ -493,9 +449,9 @@ public class FloatDict {
}
/** The key for a max value, or null if everything is NaN (no max). */
/** The key for a max value; null if empty or everything is NaN (no max). */
public String maxKey() {
checkMinMax("maxKey");
//checkMinMax("maxKey");
int index = maxIndex();
if (index == -1) {
return null;
@@ -504,9 +460,9 @@ public class FloatDict {
}
/** The max value. (Or NaN if they're all NaN.) */
/** The max value. (Or NaN if no entries or they're all NaN.) */
public float maxValue() {
checkMinMax("maxValue");
//checkMinMax("maxValue");
int index = maxIndex();
if (index == -1) {
return Float.NaN;
@@ -573,24 +529,11 @@ public class FloatDict {
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], Integer.valueOf(a));
indices.put(keys[b], Integer.valueOf(b));
// indices.put(keys[a], Integer.valueOf(a));
// indices.put(keys[b], Integer.valueOf(b));
}
// abstract class InternalSort extends Sort {
// @Override
// public int size() {
// return count;
// }
//
// @Override
// public void swap(int a, int b) {
// FloatHash.this.swap(a, b);
// }
// }
/**
* Sort the keys alphabetically (ignoring case). Uses the value as a
* tie-breaker (only really possible with a key that has a case change).
@@ -599,36 +542,16 @@ public class FloatDict {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
sortImpl(true, false);
// new InternalSort() {
// @Override
// public float compare(int a, int b) {
// int result = keys[a].compareToIgnoreCase(keys[b]);
// if (result != 0) {
// return result;
// }
// return values[b] - values[a];
// }
// }.run();
sortImpl(true, false, true);
}
/**
* @webref floatdict:method
* @brief Sort the keys alphabetially in reverse
* @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
sortImpl(true, true);
// new InternalSort() {
// @Override
// public float compare(int a, int b) {
// int result = keys[b].compareToIgnoreCase(keys[a]);
// if (result != 0) {
// return result;
// }
// return values[a] - values[b];
// }
// }.run();
sortImpl(true, true, true);
}
@@ -639,13 +562,17 @@ public class FloatDict {
* @brief Sort by values in ascending order
*/
public void sortValues() {
sortImpl(false, false);
// new InternalSort() {
// @Override
// public float compare(int a, int b) {
//
// }
// }.run();
sortValues(true);
}
/**
* Set true to ensure that the order returned is identical. Slightly
* slower because the tie-breaker for identical values compares the keys.
* @param stable
*/
public void sortValues(boolean stable) {
sortImpl(false, false, stable);
}
@@ -654,50 +581,17 @@ public class FloatDict {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
sortImpl(false, true);
// new InternalSort() {
// @Override
// public float compare(int a, int b) {
// float diff = values[b] - values[a];
// if (diff == 0 && keys[a] != null && keys[b] != null) {
// diff = keys[a].compareToIgnoreCase(keys[b]);
// }
// return descending ? diff : -diff;
// }
// }.run();
sortValuesReverse(true);
}
// // ascending puts the largest value at the end
// // descending puts the largest value at 0
// public void sortValues(final boolean descending, final boolean tiebreaker) {
// Sort s = new Sort() {
// @Override
// public int size() {
// return count;
// }
//
// @Override
// public float compare(int a, int b) {
// float diff = values[b] - values[a];
// if (tiebreaker) {
// if (diff == 0) {
// diff = keys[a].compareToIgnoreCase(keys[b]);
// }
// }
// return descending ? diff : -diff;
// }
//
// @Override
// public void swap(int a, int b) {
// FloatHash.this.swap(a, b);
// }
// };
// s.run();
// }
public void sortValuesReverse(boolean stable) {
sortImpl(false, true, stable);
}
protected void sortImpl(final boolean useKeys, final boolean reverse) {
protected void sortImpl(final boolean useKeys, final boolean reverse,
final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
@@ -731,11 +625,11 @@ public class FloatDict {
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
return values[a] - values[b];
diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
if (diff == 0) {
if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
@@ -748,18 +642,24 @@ public class FloatDict {
}
};
s.run();
// Set the indices after sort/swaps (performance fix 160411)
indices = new HashMap<String, Integer>();
for (int i = 0; i < count; i++) {
indices.put(keys[i], i);
}
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
* @return a Dict with the original keys, mapped to their pct of the total
* @return a FloatDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
double sum = 0;
for (float value : valueArray()) {
sum += value;
for (int i = 0; i < count; i++) {
sum += values[i];
}
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {

View File

@@ -133,74 +133,20 @@ public class IntDict {
}
// private void crop() {
// if (count != keys.length) {
// keys = PApplet.subset(keys, 0, count);
// values = PApplet.subset(values, 0, count);
// }
// }
protected void crop() {
if (count != keys.length) {
keys = PApplet.subset(keys, 0, count);
values = PApplet.subset(values, 0, count);
}
}
/**
* Return the internal array being used to store the keys. Allocated but
* unused entries will be removed. This array should not be modified.
*
* @webref intdict:method
* @brief Return the internal array being used to store the keys
*/
// public String[] keys() {
// crop();
// return keys;
// }
// public Iterable<String> keys() {
// return new Iterable<String>() {
//
// @Override
// public Iterator<String> iterator() {
// return new Iterator<String>() {
// int index = -1;
//
// public void remove() {
// removeIndex(index);
// }
//
// public String next() {
// return key(++index);
// }
//
// public boolean hasNext() {
// return index+1 < size();
// }
// };
// }
// };
// }
// Use this with 'for' loops
public Iterable<String> keys() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return keyIterator();
// return new Iterator<String>() {
// int index = -1;
//
// public void remove() {
// removeIndex(index);
// }
//
// public String next() {
// return key(++index);
// }
//
// public boolean hasNext() {
// return index+1 < size();
// }
// };
}
};
}
@@ -233,6 +179,7 @@ public class IntDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
crop();
return keyArray(null);
}
@@ -292,6 +239,7 @@ public class IntDict {
* @brief Create a new array and copy each of the values into it
*/
public int[] valueArray() {
crop();
return valueArray(null);
}
@@ -428,7 +376,9 @@ public class IntDict {
// return the index of the minimum value
public int minIndex() {
checkMinMax("minIndex");
//checkMinMax("minIndex");
if (count == 0) return -1;
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -441,23 +391,30 @@ public class IntDict {
}
// return the minimum value
// return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
int index = minIndex();
if (index == -1) {
return null;
}
return keys[index];
}
// return the minimum value, or throw an error if there are no values
public int minValue() {
checkMinMax("minValue");
return values[minIndex()];
}
// return the key for the minimum value
public String minKey() {
checkMinMax("minKey");
return keys[minIndex()];
}
// return the index of the max value
public int maxIndex() {
checkMinMax("maxIndex");
//checkMinMax("maxIndex");
if (count == 0) {
return -1;
}
int index = 0;
int value = values[0];
for (int i = 1; i < count; i++) {
@@ -470,17 +427,21 @@ public class IntDict {
}
// return the maximum value
public int maxValue() {
checkMinMax("maxValue");
return values[maxIndex()];
/** return the key corresponding to the maximum value or null if no entries */
public String maxKey() {
//checkMinMax("maxKey");
int index = maxIndex();
if (index == -1) {
return null;
}
return keys[index];
}
// return the key corresponding to the maximum value
public String maxKey() {
checkMinMax("maxKey");
return keys[maxIndex()];
// return the maximum value or throw an error if zero length
public int maxValue() {
checkMinMax("maxIndex");
return values[maxIndex()];
}
@@ -541,8 +502,8 @@ public class IntDict {
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], Integer.valueOf(a));
indices.put(keys[b], Integer.valueOf(b));
// indices.put(keys[a], Integer.valueOf(a));
// indices.put(keys[b], Integer.valueOf(b));
}
@@ -554,7 +515,7 @@ public class IntDict {
* @brief Sort the keys alphabetically
*/
public void sortKeys() {
sortImpl(true, false);
sortImpl(true, false, true);
}
/**
@@ -562,10 +523,10 @@ public class IntDict {
* tie-breaker (only really possible with a key that has a case change).
*
* @webref intdict:method
* @brief Sort the keys alphabetially in reverse
* @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
sortImpl(true, true);
sortImpl(true, true, true);
}
@@ -576,9 +537,20 @@ public class IntDict {
* @brief Sort by values in ascending order
*/
public void sortValues() {
sortImpl(false, false);
sortValues(true);
}
/**
* Set true to ensure that the order returned is identical. Slightly
* slower because the tie-breaker for identical values compares the keys.
* @param stable
*/
public void sortValues(boolean stable) {
sortImpl(false, false, stable);
}
/**
* Sort by values in descending order. The largest value will be at [0].
*
@@ -586,11 +558,17 @@ public class IntDict {
* @brief Sort by values in descending order
*/
public void sortValuesReverse() {
sortImpl(false, true);
sortValuesReverse(true);
}
protected void sortImpl(final boolean useKeys, final boolean reverse) {
public void sortValuesReverse(boolean stable) {
sortImpl(false, true, stable);
}
protected void sortImpl(final boolean useKeys, final boolean reverse,
final boolean stable) {
Sort s = new Sort() {
@Override
public int size() {
@@ -603,11 +581,11 @@ public class IntDict {
if (useKeys) {
diff = keys[a].compareToIgnoreCase(keys[b]);
if (diff == 0) {
return values[a] - values[b];
diff = values[a] - values[b];
}
} else { // sort values
diff = values[a] - values[b];
if (diff == 0) {
if (diff == 0 && stable) {
diff = keys[a].compareToIgnoreCase(keys[b]);
}
}
@@ -620,18 +598,24 @@ public class IntDict {
}
};
s.run();
// Set the indices after sort/swaps (performance fix 160411)
indices = new HashMap<String, Integer>();
for (int i = 0; i < count; i++) {
indices.put(keys[i], i);
}
}
/**
* Sum all of the values in this dictionary, then return a new FloatDict of
* each key, divided by the total sum. The total for all values will be ~1.0.
* @return a Dict with the original keys, mapped to their pct of the total
* @return an IntDict with the original keys, mapped to their pct of the total
*/
public FloatDict getPercent() {
double sum = 0;
for (int value : valueArray()) {
sum += value;
for (int i = 0; i < count; i++) {
sum += values[i];
}
FloatDict outgoing = new FloatDict();
for (int i = 0; i < size(); i++) {
@@ -655,6 +639,13 @@ public class IntDict {
}
public void print() {
for (int i = 0; i < size(); i++) {
System.out.println(keys[i] + " = " + values[i]);
}
}
/**
* Write tab-delimited entries out to
* @param writer
@@ -667,13 +658,6 @@ public class IntDict {
}
public void print() {
for (int i = 0; i < size(); i++) {
System.out.println(keys[i] + " = " + values[i]);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@@ -142,39 +142,32 @@ public class StringDict {
}
// /**
// * Return the internal array being used to store the keys. Allocated but
// * unused entries will be removed. This array should not be modified.
// */
// public String[] keys() {
// crop();
// return keys;
// }
/**
* @webref stringdict:method
* @brief Return the internal array being used to store the keys
*/
public Iterable<String> keys() {
return new Iterable<String>() {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
int index = -1;
return keyIterator();
}
};
}
public void remove() {
removeIndex(index);
}
public String next() {
return key(++index);
}
// Use this to iterate when you want to be able to remove elements along the way
public Iterator<String> keyIterator() {
return new Iterator<String>() {
int index = -1;
public boolean hasNext() {
return index+1 < size();
}
};
public void remove() {
removeIndex(index);
}
public String next() {
return key(++index);
}
public boolean hasNext() {
return index+1 < size();
}
};
}
@@ -187,6 +180,7 @@ public class StringDict {
* @brief Return a copy of the internal keys array
*/
public String[] keyArray() {
crop();
return keyArray(null);
}
@@ -213,21 +207,26 @@ public class StringDict {
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
int index = -1;
return valueIterator();
}
};
}
public void remove() {
removeIndex(index);
}
public String next() {
return value(++index);
}
public Iterator<String> valueIterator() {
return new Iterator<String>() {
int index = -1;
public boolean hasNext() {
return index+1 < size();
}
};
public void remove() {
removeIndex(index);
}
public String next() {
return value(++index);
}
public boolean hasNext() {
return index+1 < size();
}
};
}
@@ -240,6 +239,7 @@ public class StringDict {
* @brief Create a new array and copy each of the values into it
*/
public String[] valueArray() {
crop();
return valueArray(null);
}
@@ -357,8 +357,8 @@ public class StringDict {
keys[b] = tkey;
values[b] = tvalue;
indices.put(keys[a], Integer.valueOf(a));
indices.put(keys[b], Integer.valueOf(b));
// indices.put(keys[a], Integer.valueOf(a));
// indices.put(keys[b], Integer.valueOf(b));
}
@@ -375,7 +375,7 @@ public class StringDict {
/**
* @webref stringdict:method
* @brief Sort the keys alphabetially in reverse
* @brief Sort the keys alphabetically in reverse
*/
public void sortKeysReverse() {
sortImpl(true, true);
@@ -432,6 +432,12 @@ public class StringDict {
}
};
s.run();
// Set the indices after sort/swaps (performance fix 160411)
indices = new HashMap<String, Integer>();
for (int i = 0; i < count; i++) {
indices.put(keys[i], i);
}
}
@@ -448,6 +454,13 @@ public class StringDict {
}
public void print() {
for (int i = 0; i < size(); i++) {
System.out.println(keys[i] + " = " + values[i]);
}
}
/**
* Write tab-delimited entries out to
* @param writer
@@ -460,13 +473,6 @@ public class StringDict {
}
public void print() {
for (int i = 0; i < size(); i++) {
System.out.println(keys[i] + " = " + values[i]);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();

View File

@@ -1,4 +1,13 @@
0249 (3.0.3)
X Float/IntDict changes
X minIndex() and maxIndex() return -1 when count is zero (rather than ex)
X bug fix to reverse sorts
X 2x performance increase for sorting
X added optional "stable" parameter for sorting by values
X set to false for higher performance
X call crop() in keyArray() and valueArray() functions
X they're duplicates, their length is an implementation detail
X normalize features and error handling between all of them
_ add push() and pop() methods to mirror js?