All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.jackrabbit.commons.flat.Rank Maven / Gradle / Ivy

There is a newer version: 2024.11.18751.20241128T090041Z-241100
Show newest version
/*
 * 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;

/**
 * 

* This class does efficient ranking of values of type T wrt. to a * {@link Comparator} for T. After creating an instance of * Rank, the {@link #take(int)} method returns the next * k smallest values. That is, each of these values is smaller than * every value not yet retrieved. The order of the values returned by * take is not specified in general. However if the values are in * increasing order, the values returned by take will also be in * increasing order. *

*

* Note: The values may not contain duplicates or the behavior * of take is not defined. *

* * @param Type of values in this Rank. */ public class Rank { private final T[] values; private final Comparator order; private int first; /** * Create a new instance of Rank for a given array of * values and a given order. The * values are manipulated in place, no copying is performed. * * @param values values for ranking. Duplicates are not allowed. * @param order Ordering for ranking */ public Rank(T[] values, Comparator order) { super(); this.values = values; this.order = order; } /** * Create a new instance of Rank for a given collection of * values and a given order. The * values are copied into an internal array before they are * manipulated. * * @param values values for ranking. Duplicates are not allowed. * @param componentType type evidence for the values * @param order Ordering for ranking */ public Rank(Collection values, Class componentType, Comparator order) { super(); this.values = toArray(values, componentType); this.order = order; } /** * Create a new instance of Rank for the first * count values in a a given iterator of values * and a given order. The values are copied into * an internal array before they are manipulated. * * @param values values for ranking. Duplicates are not allowed. * @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 values, Class componentType, int count, Comparator 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 l = new LinkedList(); while (values.hasNext()) { l.add(values.next()); } this.values = toArray(l, componentType); } } /** * Create a new instance of Rank for a given array of * values. The order is determined by the natural ordering of * the values (i.e. through {@link Comparable}). The values are * manipulated in place, no copying is performed. * * @param extends Comparable<S> * @param values values for ranking. Duplicates are not allowed. * @return A new instance of Rank. */ public static > Rank rank(S[] values) { return new Rank(values, Rank.comparableComparator()); } /** * Create a new instance of Rank for a given collection of * values. The order is determined by the natural ordering of * the values (i.e. through {@link Comparable}). The values are * copied into an internal array before they are manipulated. * * @param extends Comparable<S> * @param values values for ranking. Duplicates are not allowed. * @param componentType type evidence for the values * @return A new instance of Rank. */ public static > Rank rank(Collection values, Class componentType) { return new Rank(values, componentType, Rank.comparableComparator()); } /** * Create a new instance of Rank for the first * count values in a a given iterator of values. * The order is determined by the natural ordering of the values (i.e. * through {@link Comparable}). The values are copied into an * internal array before they are manipulated. * * @param extends Comparable<S> * @param values values for ranking. Duplicates are not allowed. * @param componentType type evidence for the values * @param count Number of items to include. -1 for all. * @return A new instance of Rank. */ public static > Rank rank(Iterator values, Class componentType, int count) { return new Rank(values, componentType, count, Rank.comparableComparator()); } /** * Utility method for creating a {@link Comparator} of T from a * {@link Comparable} of type T. * * @param extends Comparable<T> * @return Comparator whose order is defined by T. */ public static > Comparator comparableComparator() { return new Comparator() { public int compare(T c1, T c2) { return c1.compareTo(c2); } }; } public Comparator getOrder() { return order; } /** * Returns the n-th smallest values remaining in this * Rank. * * @param n Number of values to return * @return An iterator containing the next n smallest values. * @throws NoSuchElementException if this Rank has not enough * remaining elements or when n is negative. */ public Iterator 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.emptySet().iterator(); } } /** * Returns the number of remaining items in the Rank. * * @return number of remaining items. */ public int size() { return values.length - first; } // -----------------------------------------------------< internal >--- /** * Rearrange {@link #values} such that each of the n first * values starting at from is smaller that all the remaining * items up to to. */ 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[] toArray(Collection collection, Class componentType) { return collection.toArray(createArray(collection.size(), componentType)); } @SuppressWarnings("unchecked") private static S[] createArray(int size, Class componentType) { return (S[]) Array.newInstance(componentType, size); } }