SortedKeyedList...: Support arbitrary comparators

Signed-off-by: Samuel Holland <samuel@sholland.org>
This commit is contained in:
Samuel Holland 2018-01-06 23:44:34 -06:00
parent 4f2b6bef84
commit 536c6958fc
3 changed files with 119 additions and 8 deletions

View File

@ -5,7 +5,11 @@ import android.support.annotation.NonNull;
import java.util.AbstractList; import java.util.AbstractList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Spliterator;
/** /**
* KeyedArrayList that enforces uniqueness and sorted order across the set of keys. This class uses * KeyedArrayList that enforces uniqueness and sorted order across the set of keys. This class uses
@ -14,9 +18,28 @@ import java.util.List;
* key still require O(n) time. * key still require O(n) time.
*/ */
public class ObservableSortedKeyedArrayList<K extends Comparable<? super K>, public class ObservableSortedKeyedArrayList<K, E extends Keyed<? extends K>>
E extends Keyed<? extends K>> extends ObservableKeyedArrayList<K, E> { extends ObservableKeyedArrayList<K, E> implements ObservableSortedKeyedList<K, E> {
private final transient List<K> keyList = new KeyList<>(this); private final Comparator<? super K> comparator;
private final transient KeyList<K, E> keyList = new KeyList<>(this);
public ObservableSortedKeyedArrayList() {
comparator = null;
}
public ObservableSortedKeyedArrayList(final Comparator<? super K> comparator) {
this.comparator = comparator;
}
public ObservableSortedKeyedArrayList(final Collection<? extends E> c) {
this();
addAll(c);
}
public ObservableSortedKeyedArrayList(final SortedKeyedList<K, E> other) {
this(other.comparator());
addAll(other);
}
@Override @Override
public boolean add(final E e) { public boolean add(final E e) {
@ -57,25 +80,71 @@ public class ObservableSortedKeyedArrayList<K extends Comparable<? super K>,
return true; return true;
} }
@Override
public Comparator<? super K> comparator() {
return comparator;
}
@Override
public K firstKey() {
if (isEmpty())
throw new NoSuchElementException();
return get(0).getKey();
}
private int getInsertionPoint(final E e) { private int getInsertionPoint(final E e) {
return -Collections.binarySearch(keyList, e.getKey()) - 1; if (comparator != null) {
return -Collections.binarySearch(keyList, e.getKey(), comparator) - 1;
} else {
@SuppressWarnings("unchecked") final List<Comparable<? super K>> list =
(List<Comparable<? super K>>) keyList;
return -Collections.binarySearch(list, e.getKey()) - 1;
}
} }
@Override @Override
public int indexOfKey(final K key) { public int indexOfKey(final K key) {
final int index = Collections.binarySearch(keyList, key); final int index;
if (comparator != null) {
index = Collections.binarySearch(keyList, key, comparator);
} else {
@SuppressWarnings("unchecked") final List<Comparable<? super K>> list =
(List<Comparable<? super K>>) keyList;
index = Collections.binarySearch(list, key);
}
return index >= 0 ? index : -1; return index >= 0 ? index : -1;
} }
@Override
@NonNull
public Set<K> keySet() {
return keyList;
}
@Override @Override
public int lastIndexOfKey(final K key) { public int lastIndexOfKey(final K key) {
// There can never be more than one element with the same key in the list. // There can never be more than one element with the same key in the list.
return indexOfKey(key); return indexOfKey(key);
} }
@Override
public K lastKey() {
if (isEmpty())
throw new NoSuchElementException();
return get(size() - 1).getKey();
}
@Override @Override
public E set(final int index, final E e) { public E set(final int index, final E e) {
if (e.getKey().compareTo(get(index).getKey()) != 0) { final int order;
if (comparator != null) {
order = comparator.compare(e.getKey(), get(index).getKey());
} else {
@SuppressWarnings("unchecked") final Comparable<? super K> key =
(Comparable<? super K>) e.getKey();
order = key.compareTo(get(index).getKey());
}
if (order != 0) {
// Allow replacement if the new key would be inserted adjacent to the replaced element. // Allow replacement if the new key would be inserted adjacent to the replaced element.
final int insertionPoint = getInsertionPoint(e); final int insertionPoint = getInsertionPoint(e);
if (insertionPoint < index || insertionPoint > index + 1) if (insertionPoint < index || insertionPoint > index + 1)
@ -84,8 +153,14 @@ public class ObservableSortedKeyedArrayList<K extends Comparable<? super K>,
return super.set(index, e); return super.set(index, e);
} }
private static final class KeyList<K extends Comparable<? super K>, @Override
E extends Keyed<? extends K>> extends AbstractList<K> { @NonNull
public Collection<E> values() {
return this;
}
private static final class KeyList<K, E extends Keyed<? extends K>>
extends AbstractList<K> implements Set<K> {
private final ObservableSortedKeyedArrayList<K, E> list; private final ObservableSortedKeyedArrayList<K, E> list;
private KeyList(final ObservableSortedKeyedArrayList<K, E> list) { private KeyList(final ObservableSortedKeyedArrayList<K, E> list) {
@ -101,5 +176,10 @@ public class ObservableSortedKeyedArrayList<K extends Comparable<? super K>,
public int size() { public int size() {
return list.size(); return list.size();
} }
@Override
public Spliterator<K> spliterator() {
return super.spliterator();
}
} }
} }

View File

@ -0,0 +1,9 @@
package com.wireguard.android.util;
/**
* A list that is both sorted/keyed and observable.
*/
public interface ObservableSortedKeyedList<K, E extends Keyed<? extends K>>
extends ObservableKeyedList<K, E>, SortedKeyedList<K, E> {
}

View File

@ -0,0 +1,22 @@
package com.wireguard.android.util;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
/**
* A keyed list where all elements are sorted by the comparator returned by {@code comparator()}
* applied to their keys.
*/
public interface SortedKeyedList<K, E extends Keyed<? extends K>> extends KeyedList<K, E> {
Comparator<? super K> comparator();
K firstKey();
Set<K> keySet();
K lastKey();
Collection<E> values();
}