From 536c6958fc1895638721befd0b70a3ff082bcc22 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sat, 6 Jan 2018 23:44:34 -0600 Subject: [PATCH] SortedKeyedList...: Support arbitrary comparators Signed-off-by: Samuel Holland --- .../util/ObservableSortedKeyedArrayList.java | 96 +++++++++++++++++-- .../util/ObservableSortedKeyedList.java | 9 ++ .../android/util/SortedKeyedList.java | 22 +++++ 3 files changed, 119 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java create mode 100644 app/src/main/java/com/wireguard/android/util/SortedKeyedList.java diff --git a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java index 9ccc1c10..d4dff389 100644 --- a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java +++ b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedArrayList.java @@ -5,7 +5,11 @@ import android.support.annotation.NonNull; import java.util.AbstractList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; 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 @@ -14,9 +18,28 @@ import java.util.List; * key still require O(n) time. */ -public class ObservableSortedKeyedArrayList, - E extends Keyed> extends ObservableKeyedArrayList { - private final transient List keyList = new KeyList<>(this); +public class ObservableSortedKeyedArrayList> + extends ObservableKeyedArrayList implements ObservableSortedKeyedList { + private final Comparator comparator; + private final transient KeyList keyList = new KeyList<>(this); + + public ObservableSortedKeyedArrayList() { + comparator = null; + } + + public ObservableSortedKeyedArrayList(final Comparator comparator) { + this.comparator = comparator; + } + + public ObservableSortedKeyedArrayList(final Collection c) { + this(); + addAll(c); + } + + public ObservableSortedKeyedArrayList(final SortedKeyedList other) { + this(other.comparator()); + addAll(other); + } @Override public boolean add(final E e) { @@ -57,25 +80,71 @@ public class ObservableSortedKeyedArrayList, return true; } + @Override + public Comparator comparator() { + return comparator; + } + + @Override + public K firstKey() { + if (isEmpty()) + throw new NoSuchElementException(); + return get(0).getKey(); + } + 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> list = + (List>) keyList; + return -Collections.binarySearch(list, e.getKey()) - 1; + } } @Override 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> list = + (List>) keyList; + index = Collections.binarySearch(list, key); + } return index >= 0 ? index : -1; } + @Override + @NonNull + public Set keySet() { + return keyList; + } + @Override public int lastIndexOfKey(final K key) { // There can never be more than one element with the same key in the list. return indexOfKey(key); } + @Override + public K lastKey() { + if (isEmpty()) + throw new NoSuchElementException(); + return get(size() - 1).getKey(); + } + @Override 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 key = + (Comparable) 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. final int insertionPoint = getInsertionPoint(e); if (insertionPoint < index || insertionPoint > index + 1) @@ -84,8 +153,14 @@ public class ObservableSortedKeyedArrayList, return super.set(index, e); } - private static final class KeyList, - E extends Keyed> extends AbstractList { + @Override + @NonNull + public Collection values() { + return this; + } + + private static final class KeyList> + extends AbstractList implements Set { private final ObservableSortedKeyedArrayList list; private KeyList(final ObservableSortedKeyedArrayList list) { @@ -101,5 +176,10 @@ public class ObservableSortedKeyedArrayList, public int size() { return list.size(); } + + @Override + public Spliterator spliterator() { + return super.spliterator(); + } } } diff --git a/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java new file mode 100644 index 00000000..56d10c17 --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/ObservableSortedKeyedList.java @@ -0,0 +1,9 @@ +package com.wireguard.android.util; + +/** + * A list that is both sorted/keyed and observable. + */ + +public interface ObservableSortedKeyedList> + extends ObservableKeyedList, SortedKeyedList { +} diff --git a/app/src/main/java/com/wireguard/android/util/SortedKeyedList.java b/app/src/main/java/com/wireguard/android/util/SortedKeyedList.java new file mode 100644 index 00000000..b164b99d --- /dev/null +++ b/app/src/main/java/com/wireguard/android/util/SortedKeyedList.java @@ -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> extends KeyedList { + Comparator comparator(); + + K firstKey(); + + Set keySet(); + + K lastKey(); + + Collection values(); +}