summaryrefslogtreecommitdiffstats
path: root/guava/src/com/google/common/collect/SortedLists.java
diff options
context:
space:
mode:
Diffstat (limited to 'guava/src/com/google/common/collect/SortedLists.java')
-rw-r--r--guava/src/com/google/common/collect/SortedLists.java284
1 files changed, 284 insertions, 0 deletions
diff --git a/guava/src/com/google/common/collect/SortedLists.java b/guava/src/com/google/common/collect/SortedLists.java
new file mode 100644
index 0000000..6fc0d71
--- /dev/null
+++ b/guava/src/com/google/common/collect/SortedLists.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2010 The Guava Authors
+ *
+ * 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.google.common.collect;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.GwtCompatible;
+import com.google.common.base.Function;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.RandomAccess;
+
+import javax.annotation.Nullable;
+
+/**
+ * Static methods pertaining to sorted {@link List} instances.
+ *
+ * In this documentation, the terms <i>greatest</i>, <i>greater</i>, <i>least</i>, and
+ * <i>lesser</i> are considered to refer to the comparator on the elements, and the terms
+ * <i>first</i> and <i>last</i> are considered to refer to the elements' ordering in a
+ * list.
+ *
+ * @author Louis Wasserman
+ */
+@GwtCompatible
+@Beta final class SortedLists {
+ private SortedLists() {}
+
+ /**
+ * A specification for which index to return if the list contains at least one element that
+ * compares as equal to the key.
+ */
+ public enum KeyPresentBehavior {
+ /**
+ * Return the index of any list element that compares as equal to the key. No guarantees are
+ * made as to which index is returned, if more than one element compares as equal to the key.
+ */
+ ANY_PRESENT {
+ @Override
+ <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
+ return foundIndex;
+ }
+ },
+ /**
+ * Return the index of the last list element that compares as equal to the key.
+ */
+ LAST_PRESENT {
+ @Override
+ <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
+ // Of course, we have to use binary search to find the precise
+ // breakpoint...
+ int lower = foundIndex;
+ int upper = list.size() - 1;
+ // Everything between lower and upper inclusive compares at >= 0.
+ while (lower < upper) {
+ int middle = (lower + upper + 1) >>> 1;
+ int c = comparator.compare(list.get(middle), key);
+ if (c > 0) {
+ upper = middle - 1;
+ } else { // c == 0
+ lower = middle;
+ }
+ }
+ return lower;
+ }
+ },
+ /**
+ * Return the index of the first list element that compares as equal to the key.
+ */
+ FIRST_PRESENT {
+ @Override
+ <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
+ // Of course, we have to use binary search to find the precise
+ // breakpoint...
+ int lower = 0;
+ int upper = foundIndex;
+ // Of course, we have to use binary search to find the precise breakpoint...
+ // Everything between lower and upper inclusive compares at <= 0.
+ while (lower < upper) {
+ int middle = (lower + upper) >>> 1;
+ int c = comparator.compare(list.get(middle), key);
+ if (c < 0) {
+ lower = middle + 1;
+ } else { // c == 0
+ upper = middle;
+ }
+ }
+ return lower;
+ }
+ },
+ /**
+ * Return the index of the first list element that compares as greater than the key, or {@code
+ * list.size()} if there is no such element.
+ */
+ FIRST_AFTER {
+ @Override
+ public <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
+ return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1;
+ }
+ },
+ /**
+ * Return the index of the last list element that compares as less than the key, or {@code -1}
+ * if there is no such element.
+ */
+ LAST_BEFORE {
+ @Override
+ public <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
+ return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1;
+ }
+ };
+ abstract <E> int resultIndex(
+ Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex);
+ }
+
+ /**
+ * A specification for which index to return if the list contains no elements that compare as
+ * equal to the key.
+ */
+ public enum KeyAbsentBehavior {
+ /**
+ * Return the index of the next lower element in the list, or {@code -1} if there is no such
+ * element.
+ */
+ NEXT_LOWER {
+ @Override
+ int resultIndex(int higherIndex) {
+ return higherIndex - 1;
+ }
+ },
+ /**
+ * Return the index of the next higher element in the list, or {@code list.size()} if there is
+ * no such element.
+ */
+ NEXT_HIGHER {
+ @Override
+ public int resultIndex(int higherIndex) {
+ return higherIndex;
+ }
+ },
+ /**
+ * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at
+ * which the key would be inserted into the list: the index of the next higher element in the
+ * list, or {@code list.size()} if there is no such element.
+ *
+ * <p>Note that the return value will be {@code >= 0} if and only if there is an element of the
+ * list that compares as equal to the key.
+ *
+ * <p>This is equivalent to the behavior of
+ * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't present, since
+ * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}.
+ */
+ INVERTED_INSERTION_INDEX {
+ @Override
+ public int resultIndex(int higherIndex) {
+ return ~higherIndex;
+ }
+ };
+
+ abstract int resultIndex(int higherIndex);
+ }
+
+ /**
+ * Searches the specified naturally ordered list for the specified object using the binary search
+ * algorithm.
+ *
+ * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
+ * KeyAbsentBehavior)} using {@link Ordering#natural}.
+ */
+ public static <E extends Comparable> int binarySearch(List<? extends E> list, E e,
+ KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) {
+ checkNotNull(e);
+ return binarySearch(
+ list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior);
+ }
+
+ /**
+ * Binary searches the list for the specified key, using the specified key function.
+ *
+ * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
+ * KeyAbsentBehavior)} using {@link Ordering#natural}.
+ */
+ public static <E, K extends Comparable> int binarySearch(List<E> list,
+ Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior,
+ KeyAbsentBehavior absentBehavior) {
+ return binarySearch(
+ list,
+ keyFunction,
+ key,
+ Ordering.natural(),
+ presentBehavior,
+ absentBehavior);
+ }
+
+ /**
+ * Binary searches the list for the specified key, using the specified key function.
+ *
+ * <p>Equivalent to
+ * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} using
+ * {@link Lists#transform(List, Function) Lists.transform(list, keyFunction)}.
+ */
+ public static <E, K> int binarySearch(
+ List<E> list,
+ Function<? super E, K> keyFunction,
+ @Nullable K key,
+ Comparator<? super K> keyComparator,
+ KeyPresentBehavior presentBehavior,
+ KeyAbsentBehavior absentBehavior) {
+ return binarySearch(
+ Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior);
+ }
+
+ /**
+ * Searches the specified list for the specified object using the binary search algorithm. The
+ * list must be sorted into ascending order according to the specified comparator (as by the
+ * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior
+ * to making this call. If it is not sorted, the results are undefined.
+ *
+ * <p>If there are elements in the list which compare as equal to the key, the choice of
+ * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to
+ * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned.
+ *
+ * <p>This method runs in log(n) time on random-access lists, which offer near-constant-time
+ * access to each list element.
+ *
+ * @param list the list to be searched.
+ * @param key the value to be searched for.
+ * @param comparator the comparator by which the list is ordered.
+ * @param presentBehavior the specification for what to do if at least one element of the list
+ * compares as equal to the key.
+ * @param absentBehavior the specification for what to do if no elements of the list compare as
+ * equal to the key.
+ * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list;
+ * otherwise the index determined by the {@code KeyAbsentBehavior}.
+ */
+ public static <E> int binarySearch(List<? extends E> list, @Nullable E key,
+ Comparator<? super E> comparator, KeyPresentBehavior presentBehavior,
+ KeyAbsentBehavior absentBehavior) {
+ checkNotNull(comparator);
+ checkNotNull(list);
+ checkNotNull(presentBehavior);
+ checkNotNull(absentBehavior);
+ if (!(list instanceof RandomAccess)) {
+ list = Lists.newArrayList(list);
+ }
+ // TODO(user): benchmark when it's best to do a linear search
+
+ int lower = 0;
+ int upper = list.size() - 1;
+
+ while (lower <= upper) {
+ int middle = (lower + upper) >>> 1;
+ int c = comparator.compare(key, list.get(middle));
+ if (c < 0) {
+ upper = middle - 1;
+ } else if (c > 0) {
+ lower = middle + 1;
+ } else {
+ return lower + presentBehavior.resultIndex(
+ comparator, key, list.subList(lower, upper + 1), middle - lower);
+ }
+ }
+ return absentBehavior.resultIndex(lower);
+ }
+}