diff --git a/app/build.gradle b/app/build.gradle index 8833ae37e..b3c143641 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -108,7 +108,6 @@ dependencies { implementation 'com.github.seven332:streampipe:0.1.0' implementation 'com.github.seven332:tuxiang:0.1.6' implementation 'com.github.seven332:unifile:9ec57bcd8f' - implementation 'com.github.seven332:yorozuya-collect:0.1.4' implementation 'com.h6ah4i.android.widget.advrecyclerview:advrecyclerview:1.0.0' implementation 'com.squareup.okhttp3:okhttp:4.10.0' implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.10.0' @@ -124,9 +123,6 @@ dependencies { configurations.all { resolutionStrategy { - exclude group: 'com.github.seven332', module: 'okhttp' - exclude group: 'com.github.seven332', module: 'image' - exclude group: 'com.github.seven332', module: 'yorozuya' exclude group: 'androidx.appcompat', module: 'appcompat' } } diff --git a/app/src/main/java/com/hippo/yorozuya/collect/AbstractMultimap.java b/app/src/main/java/com/hippo/yorozuya/collect/AbstractMultimap.java new file mode 100644 index 000000000..32e0604df --- /dev/null +++ b/app/src/main/java/com/hippo/yorozuya/collect/AbstractMultimap.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2014-2015 Markus Junginger, greenrobot (http://greenrobot.de) + * + * 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.hippo.yorozuya.collect; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * Combines a Map with List values to provide simple way to store multiple values for a key (multimap). + *
+ * Threading note: All methods are synchronized + */ +public abstract class AbstractMultimapNote that this container keeps its mappings in an array data structure, + * using a binary search to find keys. The implementation is not intended to be appropriate for + * data structures + * that may contain large numbers of items. It is generally slower than a traditional + * HashMap, since lookups require a binary search and adds and removes require inserting + * and deleting entries in the array. For containers holding up to hundreds of items, + * the performance difference is not significant, less than 50%.
+ * + *It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * keyAt(int) with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of valueAt(int).
0
+ * if no such mapping has been made.
+ */
+ public long get(int key) {
+ return get(key, 0);
+ }
+
+ /**
+ * Gets the long mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public long get(int key, long valueIfKeyNotFound) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i < 0) {
+ return valueIfKeyNotFound;
+ } else {
+ return mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ removeAt(i);
+ }
+ }
+
+ /**
+ * Removes the mapping at the given index.
+ */
+ public void removeAt(int index) {
+ System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+ mSize--;
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, long value) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ mKeys = CollectionUtils.insert(mKeys, mSize, i, key);
+ mValues = CollectionUtils.insert(mValues, mSize, i, value);
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseIntArray
+ * currently stores.
+ */
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the key from the indexth key-value mapping that this
+ * SparseIJArray stores.
+ *
+ * The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., keyAt(0) will return the
+ * smallest key and keyAt(size()-1) will return the largest
+ * key.
0...size()-1, returns
+ * the value from the indexth key-value mapping that this
+ * SparseIJArray stores.
+ *
+ * The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * valueAt(0) will return the value associated with the
+ * smallest key and valueAt(size()-1) will return the value
+ * associated with the largest key.
This implementation composes a string by iterating over its mappings.
+ */
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 28);
+ buffer.append('{');
+ for (int i=0; i Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%. To help with performance, the container includes an optimization when removing
+ * keys: instead of compacting its array immediately, it leaves the removed entry marked
+ * as deleted. The entry can then be re-used for the same key, or compacted later in
+ * a single garbage collection step of all removed entries. This garbage collection will
+ * need to be performed at any time the array needs to be grown or the the map size or
+ * entry values are retrieved. It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ * Note also that unlike most collections' {@code indexOf} methods,
+ * this method compares values using {@code ==} rather than {@code equals}.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseILArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(int key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ mKeys = CollectionUtils.append(mKeys, mSize, key);
+ mValues = CollectionUtils.append(mValues, mSize, value);
+ mSize++;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation composes a string by iterating over its mappings. If
+ * this map contains itself as a value, the string "(this Map)"
+ * will appear in its place.
+ */
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 28);
+ buffer.append('{');
+ for (int i=0; i Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%. It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * This implementation composes a string by iterating over its mappings.
+ */
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 28);
+ buffer.append('{');
+ for (int i=0; i Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys. The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items. It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array. For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%. To help with performance, the container includes an optimization when removing
+ * keys: instead of compacting its array immediately, it leaves the removed entry marked
+ * as deleted. The entry can then be re-used for the same key, or compacted later in
+ * a single garbage collection step of all removed entries. This garbage collection will
+ * need to be performed at any time the array needs to be grown or the the map size or
+ * entry values are retrieved. It is possible to iterate over the items in this container using
+ * {@link #keyAt(int)} and {@link #valueAt(int)}. Iterating over the keys using
+ * The keys corresponding to indices in ascending order are guaranteed to
+ * be in ascending order, e.g., The values corresponding to indices in ascending order are guaranteed
+ * to be associated with keys in ascending order, e.g.,
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ * Note also that unlike most collections' {@code indexOf} methods,
+ * this method compares values using {@code ==} rather than {@code equals}.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseJLArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(long key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ mKeys = CollectionUtils.append(mKeys, mSize, key);
+ mValues = CollectionUtils.append(mValues, mSize, value);
+ mSize++;
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * This implementation composes a string by iterating over its mappings. If
+ * this map contains itself as a value, the string "(this Map)"
+ * will appear in its place.
+ */
+ @Override
+ public String toString() {
+ if (size() <= 0) {
+ return "{}";
+ }
+
+ StringBuilder buffer = new StringBuilder(mSize * 28);
+ buffer.append('{');
+ for (int i=0; ikeyAt(int) with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of valueAt(int).null
+ * if no such mapping has been made.
+ */
+ public E get(int key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ @SuppressWarnings("unchecked")
+ public E get(int key, E valueIfKeyNotFound) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ @SuppressWarnings("unchecked")
+ public E delete(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ E deleted = (E) mValues[i];
+ if (deleted != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return deleted;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any, returning the old value.
+ */
+ @SuppressWarnings("unchecked")
+ public E removeReturnOld(int key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ final E old = (E) mValues[i];
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return old;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Alias for {@link #delete(int)}.
+ */
+ public E remove(int key) {
+ return delete(key);
+ }
+
+ /**
+ * Removes the mapping at the specified index.
+ */
+ public void removeAt(int index) {
+ if (mValues[index] != DELETED) {
+ mValues[index] = DELETED;
+ mGarbage = true;
+ }
+ }
+
+ /**
+ * Remove a range of mappings as a batch.
+ *
+ * @param index Index to begin at
+ * @param size Number of mappings to remove
+ */
+ public void removeAtRange(int index, int size) {
+ final int end = Math.min(mSize, index + size);
+ for (int i = index; i < end; i++) {
+ removeAt(i);
+ }
+ }
+
+ private void gc() {
+ // Log.e("SparseILArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ int[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ values[i] = null;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseILArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(int key, E value) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
+ }
+
+ mKeys = CollectionUtils.insert(mKeys, mSize, i, key);
+ mValues = CollectionUtils.insert(mValues, mSize, i, value);
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseILArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the key from the indexth key-value mapping that this
+ * SparseILArray stores.
+ *
+ * keyAt(0) will return the
+ * smallest key and keyAt(size()-1) will return the largest
+ * key.0...size()-1, returns
+ * the value from the indexth key-value mapping that this
+ * SparseILArray stores.
+ *
+ * valueAt(0) will return the value associated with the
+ * smallest key and valueAt(size()-1) will return the value
+ * associated with the largest key.0...size()-1, sets a new
+ * value for the indexth key-value mapping that this
+ * SparseILArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(int key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return ContainerHelpers.binarySearch(mKeys, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * keyAt(int) with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of valueAt(int).false
+ * if no such mapping has been made.
+ */
+ public boolean get(long key) {
+ return get(key, false);
+ }
+
+ /**
+ * Gets the boolean mapped from the specified key, or the specified value
+ * if no such mapping has been made.
+ */
+ public boolean get(long key, boolean valueIfKeyNotFound) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i < 0) {
+ return valueIfKeyNotFound;
+ } else {
+ return mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(long key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1));
+ System.arraycopy(mValues, i + 1, mValues, i, mSize - (i + 1));
+ mSize--;
+ }
+ }
+
+ public void removeAt(int index) {
+ System.arraycopy(mKeys, index + 1, mKeys, index, mSize - (index + 1));
+ System.arraycopy(mValues, index + 1, mValues, index, mSize - (index + 1));
+ mSize--;
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, boolean value) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ mKeys = CollectionUtils.insert(mKeys, mSize, i, key);
+ mValues = CollectionUtils.insert(mValues, mSize, i, value);
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseJBArray
+ * currently stores.
+ */
+ public int size() {
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the key from the indexth key-value mapping that this
+ * SparseJBArray stores.
+ *
+ * keyAt(0) will return the
+ * smallest key and keyAt(size()-1) will return the largest
+ * key.0...size()-1, returns
+ * the value from the indexth key-value mapping that this
+ * SparseJBArray stores.
+ *
+ * valueAt(0) will return the value associated with the
+ * smallest key and valueAt(size()-1) will return the value
+ * associated with the largest key.keyAt(int) with ascending values of the index will return the
+ * keys in ascending order, or the values corresponding to the keys in ascending
+ * order in the case of valueAt(int).null
+ * if no such mapping has been made.
+ */
+ public E get(long key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ @SuppressWarnings("unchecked")
+ public E get(long key, E valueIfKeyNotFound) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ @SuppressWarnings("unchecked")
+ public E delete(long key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ E deleted = (E) mValues[i];
+ if (deleted != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return deleted;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any, returning the old value.
+ */
+ @SuppressWarnings("unchecked")
+ public E removeReturnOld(long key) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ final E old = (E) mValues[i];
+ mValues[i] = DELETED;
+ mGarbage = true;
+ return old;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Alias for {@link #delete(long)}.
+ */
+ public E remove(long key) {
+ return delete(key);
+ }
+
+ /**
+ * Removes the mapping at the specified index.
+ */
+ public void removeAt(int index) {
+ if (mValues[index] != DELETED) {
+ mValues[index] = DELETED;
+ mGarbage = true;
+ }
+ }
+
+ /**
+ * Remove a range of mappings as a batch.
+ *
+ * @param index Index to begin at
+ * @param size Number of mappings to remove
+ */
+ public void removeAtRange(int index, int size) {
+ final int end = Math.min(mSize, index + size);
+ for (int i = index; i < end; i++) {
+ removeAt(i);
+ }
+ }
+
+ private void gc() {
+ // Log.e("SparseJLArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ long[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ values[i] = null;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseJLArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, E value) {
+ int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
+ }
+
+ mKeys = CollectionUtils.insert(mKeys, mSize, i, key);
+ mValues = CollectionUtils.insert(mValues, mSize, i, value);
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseJLArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range 0...size()-1, returns
+ * the key from the indexth key-value mapping that this
+ * SparseJLArray stores.
+ *
+ * keyAt(0) will return the
+ * smallest key and keyAt(size()-1) will return the largest
+ * key.0...size()-1, returns
+ * the value from the indexth key-value mapping that this
+ * SparseJLArray stores.
+ *
+ * valueAt(0) will return the value associated with the
+ * smallest key and valueAt(size()-1) will return the value
+ * associated with the largest key.0...size()-1, sets a new
+ * value for the indexth key-value mapping that this
+ * SparseJLArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(long key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return ContainerHelpers.binarySearch(mKeys, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ *