1/*
2 * Copyright (C) 2010 The Guava Authors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5 * in compliance with the License. You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software distributed under the License
10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11 * or implied. See the License for the specific language governing permissions and limitations under
12 * the License.
13 */
14
15package com.google.common.collect;
16
17import static com.google.common.base.Preconditions.checkNotNull;
18
19import com.google.common.annotations.Beta;
20import com.google.common.annotations.GwtCompatible;
21import com.google.common.base.Function;
22
23import java.util.Collections;
24import java.util.Comparator;
25import java.util.List;
26import java.util.RandomAccess;
27
28import javax.annotation.Nullable;
29
30/**
31 * Static methods pertaining to sorted {@link List} instances.
32 *
33 * In this documentation, the terms <i>greatest</i>, <i>greater</i>, <i>least</i>, and
34 * <i>lesser</i> are considered to refer to the comparator on the elements, and the terms
35 * <i>first</i> and <i>last</i> are considered to refer to the elements' ordering in a
36 * list.
37 *
38 * @author Louis Wasserman
39 */
40@GwtCompatible
41@Beta final class SortedLists {
42  private SortedLists() {}
43
44  /**
45   * A specification for which index to return if the list contains at least one element that
46   * compares as equal to the key.
47   */
48  public enum KeyPresentBehavior {
49    /**
50     * Return the index of any list element that compares as equal to the key. No guarantees are
51     * made as to which index is returned, if more than one element compares as equal to the key.
52     */
53    ANY_PRESENT {
54      @Override
55      <E> int resultIndex(
56          Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
57        return foundIndex;
58      }
59    },
60    /**
61     * Return the index of the last list element that compares as equal to the key.
62     */
63    LAST_PRESENT {
64      @Override
65      <E> int resultIndex(
66          Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
67        // Of course, we have to use binary search to find the precise
68        // breakpoint...
69        int lower = foundIndex;
70        int upper = list.size() - 1;
71        // Everything between lower and upper inclusive compares at >= 0.
72        while (lower < upper) {
73          int middle = (lower + upper + 1) >>> 1;
74          int c = comparator.compare(list.get(middle), key);
75          if (c > 0) {
76            upper = middle - 1;
77          } else { // c == 0
78            lower = middle;
79          }
80        }
81        return lower;
82      }
83    },
84    /**
85     * Return the index of the first list element that compares as equal to the key.
86     */
87    FIRST_PRESENT {
88      @Override
89      <E> int resultIndex(
90          Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
91        // Of course, we have to use binary search to find the precise
92        // breakpoint...
93        int lower = 0;
94        int upper = foundIndex;
95        // Of course, we have to use binary search to find the precise breakpoint...
96        // Everything between lower and upper inclusive compares at <= 0.
97        while (lower < upper) {
98          int middle = (lower + upper) >>> 1;
99          int c = comparator.compare(list.get(middle), key);
100          if (c < 0) {
101            lower = middle + 1;
102          } else { // c == 0
103            upper = middle;
104          }
105        }
106        return lower;
107      }
108    },
109    /**
110     * Return the index of the first list element that compares as greater than the key, or {@code
111     * list.size()} if there is no such element.
112     */
113    FIRST_AFTER {
114      @Override
115      public <E> int resultIndex(
116          Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
117        return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1;
118      }
119    },
120    /**
121     * Return the index of the last list element that compares as less than the key, or {@code -1}
122     * if there is no such element.
123     */
124    LAST_BEFORE {
125      @Override
126      public <E> int resultIndex(
127          Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
128        return FIRST_PRESENT.resultIndex(comparator, key, list, foundIndex) - 1;
129      }
130    };
131    abstract <E> int resultIndex(
132        Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex);
133  }
134
135  /**
136   * A specification for which index to return if the list contains no elements that compare as
137   * equal to the key.
138   */
139  public enum KeyAbsentBehavior {
140    /**
141     * Return the index of the next lower element in the list, or {@code -1} if there is no such
142     * element.
143     */
144    NEXT_LOWER {
145      @Override
146      int resultIndex(int higherIndex) {
147        return higherIndex - 1;
148      }
149    },
150    /**
151     * Return the index of the next higher element in the list, or {@code list.size()} if there is
152     * no such element.
153     */
154    NEXT_HIGHER {
155      @Override
156      public int resultIndex(int higherIndex) {
157        return higherIndex;
158      }
159    },
160    /**
161     * Return {@code ~insertionIndex}, where {@code insertionIndex} is defined as the point at
162     * which the key would be inserted into the list: the index of the next higher element in the
163     * list, or {@code list.size()} if there is no such element.
164     *
165     * <p>Note that the return value will be {@code >= 0} if and only if there is an element of the
166     * list that compares as equal to the key.
167     *
168     * <p>This is equivalent to the behavior of
169     * {@link java.util.Collections#binarySearch(List, Object)} when the key isn't present, since
170     * {@code ~insertionIndex} is equal to {@code -1 - insertionIndex}.
171     */
172    INVERTED_INSERTION_INDEX {
173      @Override
174      public int resultIndex(int higherIndex) {
175        return ~higherIndex;
176      }
177    };
178
179    abstract int resultIndex(int higherIndex);
180  }
181
182  /**
183   * Searches the specified naturally ordered list for the specified object using the binary search
184   * algorithm.
185   *
186   * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
187   * KeyAbsentBehavior)} using {@link Ordering#natural}.
188   */
189  public static <E extends Comparable> int binarySearch(List<? extends E> list, E e,
190      KeyPresentBehavior presentBehavior, KeyAbsentBehavior absentBehavior) {
191    checkNotNull(e);
192    return binarySearch(
193        list, checkNotNull(e), Ordering.natural(), presentBehavior, absentBehavior);
194  }
195
196  /**
197   * Binary searches the list for the specified key, using the specified key function.
198   *
199   * <p>Equivalent to {@link #binarySearch(List, Function, Object, Comparator, KeyPresentBehavior,
200   * KeyAbsentBehavior)} using {@link Ordering#natural}.
201   */
202  public static <E, K extends Comparable> int binarySearch(List<E> list,
203      Function<? super E, K> keyFunction, @Nullable K key, KeyPresentBehavior presentBehavior,
204      KeyAbsentBehavior absentBehavior) {
205    return binarySearch(
206        list,
207        keyFunction,
208        key,
209        Ordering.natural(),
210        presentBehavior,
211        absentBehavior);
212  }
213
214  /**
215   * Binary searches the list for the specified key, using the specified key function.
216   *
217   * <p>Equivalent to
218   * {@link #binarySearch(List, Object, Comparator, KeyPresentBehavior, KeyAbsentBehavior)} using
219   * {@link Lists#transform(List, Function) Lists.transform(list, keyFunction)}.
220   */
221  public static <E, K> int binarySearch(
222      List<E> list,
223      Function<? super E, K> keyFunction,
224      @Nullable K key,
225      Comparator<? super K> keyComparator,
226      KeyPresentBehavior presentBehavior,
227      KeyAbsentBehavior absentBehavior) {
228    return binarySearch(
229        Lists.transform(list, keyFunction), key, keyComparator, presentBehavior, absentBehavior);
230  }
231
232  /**
233   * Searches the specified list for the specified object using the binary search algorithm. The
234   * list must be sorted into ascending order according to the specified comparator (as by the
235   * {@link Collections#sort(List, Comparator) Collections.sort(List, Comparator)} method), prior
236   * to making this call. If it is not sorted, the results are undefined.
237   *
238   * <p>If there are elements in the list which compare as equal to the key, the choice of
239   * {@link KeyPresentBehavior} decides which index is returned. If no elements compare as equal to
240   * the key, the choice of {@link KeyAbsentBehavior} decides which index is returned.
241   *
242   * <p>This method runs in log(n) time on random-access lists, which offer near-constant-time
243   * access to each list element.
244   *
245   * @param list the list to be searched.
246   * @param key the value to be searched for.
247   * @param comparator the comparator by which the list is ordered.
248   * @param presentBehavior the specification for what to do if at least one element of the list
249   *        compares as equal to the key.
250   * @param absentBehavior the specification for what to do if no elements of the list compare as
251   *        equal to the key.
252   * @return the index determined by the {@code KeyPresentBehavior}, if the key is in the list;
253   *         otherwise the index determined by the {@code KeyAbsentBehavior}.
254   */
255  public static <E> int binarySearch(List<? extends E> list, @Nullable E key,
256      Comparator<? super E> comparator, KeyPresentBehavior presentBehavior,
257      KeyAbsentBehavior absentBehavior) {
258    checkNotNull(comparator);
259    checkNotNull(list);
260    checkNotNull(presentBehavior);
261    checkNotNull(absentBehavior);
262    if (!(list instanceof RandomAccess)) {
263      list = Lists.newArrayList(list);
264    }
265    // TODO(user): benchmark when it's best to do a linear search
266
267    int lower = 0;
268    int upper = list.size() - 1;
269
270    while (lower <= upper) {
271      int middle = (lower + upper) >>> 1;
272      int c = comparator.compare(key, list.get(middle));
273      if (c < 0) {
274        upper = middle - 1;
275      } else if (c > 0) {
276        lower = middle + 1;
277      } else {
278        return lower + presentBehavior.resultIndex(
279            comparator, key, list.subList(lower, upper + 1), middle - lower);
280      }
281    }
282    return absentBehavior.resultIndex(lower);
283  }
284}
285