/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this * work for additional information regarding copyright ownership. The ASF * licenses this file to You 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 org.apache.jackrabbit.commons.flat; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; /** * <p> * This class does efficient ranking of values of type <code>T</code> wrt. to a * {@link Comparator} for <code>T</code>. After creating an instance of * <code>Rank</code>, the {@link #take(int)} method returns the next * <code>k</code> smallest values. That is, each of these values is smaller than * every value not yet retrieved. The order of the values returned by * <code>take</code> is not specified in general. However if the values are in * increasing order, the values returned by <code>take</code> will also be in * increasing order. * </p> * <p> * <em>Note</em>: The values <em>may not contain duplicates</em> or the behavior * of <code>take</code> is not defined. * </p> * * @param <T> Type of values in this <code>Rank</code>. */ public class Rank<T> { private final T[] values; private final Comparator<? super T> order; private int first; /** * Create a new instance of <code>Rank</code> for a given array of * <code>values</code> and a given <code>order</code>. The * <code>values</code> are manipulated in place, no copying is performed. * * @param values values for ranking. Duplicates are <em>not allowed</em>. * @param order Ordering for ranking */ public Rank(T[] values, Comparator<? super T> order) { super(); this.values = values; this.order = order; } /** * Create a new instance of <code>Rank</code> for a given collection of * <code>values</code> and a given <code>order</code>. The * <code>values</code> are copied into an internal array before they are * manipulated. * * @param values values for ranking. Duplicates are <em>not allowed</em>. * @param componentType type evidence for the values * @param order Ordering for ranking */ public Rank(Collection<T> values, Class<T> componentType, Comparator<? super T> order) { super(); this.values = toArray(values, componentType); this.order = order; } /** * Create a new instance of <code>Rank</code> for the first * <code>count</code> values in a a given iterator of <code>values</code> * and a given <code>order</code>. The <code>values</code> are copied into * an internal array before they are manipulated. * * @param values values for ranking. Duplicates are <em>not allowed</em>. * @param componentType type evidence for the values * @param count Number of items to include. -1 for all. * @param order Ordering for ranking */ public Rank(Iterator<T> values, Class<T> componentType, int count, Comparator<? super T> order) { super(); this.order = order; if (count >= 0) { this.values = createArray(count, componentType); for (int k = 0; k < count; k++) { this.values[k] = values.next(); } } else { List<T> l = new LinkedList<T>(); while (values.hasNext()) { l.add(values.next()); } this.values = toArray(l, componentType); } } /** * Create a new instance of <code>Rank</code> for a given array of * <code>values</code>. The order is determined by the natural ordering of * the values (i.e. through {@link Comparable}). The <code>values</code> are * manipulated in place, no copying is performed. * * @param <S> extends Comparable<S> * @param values values for ranking. Duplicates are <em>not allowed</em>. * @return A new instance of <code>Rank</code>. */ public static <S extends Comparable<S>> Rank<S> rank(S[] values) { return new Rank<S>(values, Rank.<S>comparableComparator()); } /** * Create a new instance of <code>Rank</code> for a given collection of * <code>values</code>. The order is determined by the natural ordering of * the values (i.e. through {@link Comparable}). The <code>values</code> are * copied into an internal array before they are manipulated. * * @param <S> extends Comparable<S> * @param values values for ranking. Duplicates are <em>not allowed</em>. * @param componentType type evidence for the values * @return A new instance of <code>Rank</code>. */ public static <S extends Comparable<S>> Rank<S> rank(Collection<S> values, Class<S> componentType) { return new Rank<S>(values, componentType, Rank.<S>comparableComparator()); } /** * Create a new instance of <code>Rank</code> for the first * <code>count</code> values in a a given iterator of <code>values</code>. * The order is determined by the natural ordering of the values (i.e. * through {@link Comparable}). The <code>values</code> are copied into an * internal array before they are manipulated. * * @param <S> extends Comparable<S> * @param values values for ranking. Duplicates are <em>not allowed</em>. * @param componentType type evidence for the values * @param count Number of items to include. -1 for all. * @return A new instance of <code>Rank</code>. */ public static <S extends Comparable<S>> Rank<S> rank(Iterator<S> values, Class<S> componentType, int count) { return new Rank<S>(values, componentType, count, Rank.<S>comparableComparator()); } /** * Utility method for creating a {@link Comparator} of <code>T</code> from a * {@link Comparable} of type <code>T</code>. * * @param <T> extends Comparable<T> * @return Comparator whose order is defined by <code>T</code>. */ public static <T extends Comparable<T>> Comparator<T> comparableComparator() { return new Comparator<T>() { public int compare(T c1, T c2) { return c1.compareTo(c2); } }; } public Comparator<? super T> getOrder() { return order; } /** * Returns the <code>n</code>-th smallest values remaining in this * <code>Rank</code>. * * @param n Number of values to return * @return An iterator containing the next <code>n</code> smallest values. * @throws NoSuchElementException if this <code>Rank</code> has not enough * remaining elements or when <code>n</code> is negative. */ public Iterator<T> take(int n) { if (n < 0 || n + first > values.length) { throw new NoSuchElementException(); } if (n > 0) { take(n, first, values.length - 1); first += n; return Arrays.asList(values).subList(first - n, first).iterator(); } else { return Collections.<T>emptySet().iterator(); } } /** * Returns the number of remaining items in the <code>Rank</code>. * * @return number of remaining items. */ public int size() { return values.length - first; } // -----------------------------------------------------< internal >--- /** * Rearrange {@link #values} such that each of the <code>n</code> first * values starting at <code>from</code> is smaller that all the remaining * items up to <code>to</code>. */ private void take(int n, int from, int to) { // Shortcut for all values if (n >= to - from + 1) { return; } // Choosing the n-th value as pivot results in correct partitioning after one pass // for already ordered values. int pivot = from + n - 1; int lo = from; int hi = to; // Partition values around pivot while (lo < hi) { // Find values to swap around the pivot while (order.compare(values[lo], values[pivot]) < 0) { lo++; } while (order.compare(values[hi], values[pivot]) > 0) { hi--; } if (lo < hi) { // Swap values and keep track of pivot position in case the pivot itself is swapped if (lo == pivot) { pivot = hi; } else if (hi == pivot) { pivot = lo; } swap(lo, hi); lo++; hi--; } } // Actual number of values taken int nn = pivot + 1 - from; if (nn > n) { // Recurse: take first n elements from first partition take(n, from, pivot); } else if (nn < n) { // Recurse: take first n - nn elements from second partition take(n - nn, pivot + 1, to); } // else done } private void swap(int lo, int hi) { T t1 = values[lo]; T t2 = values[hi]; if (order.compare(t1, t2) == 0) { throw new IllegalStateException("Detected duplicates " + t1); } values[lo] = t2; values[hi] = t1; } // -----------------------------------------------------< utility >--- private static <S> S[] toArray(Collection<S> collection, Class<S> componentType) { return collection.toArray(createArray(collection.size(), componentType)); } @SuppressWarnings("unchecked") private static <S> S[] createArray(int size, Class<S> componentType) { return (S[]) Array.newInstance(componentType, size); } }