Из отдела "слишком много времени у меня на руках": вот небольшой класс MedianGenerator:
/**
* Methods to calculate the median value of a supplied {@link List}.
*/
public final class MedianGenerator{
private MedianGenerator(){
}
/**
* Calculate the median of a supplied list.
* <ol>
* <li>A copy will be generated</li>
* <li>this copy will be sorted with the supplied comparator</li>
* <li>the median will be calculated, using the supplied averageCalculator
* for collections with an even number of items</li>
* </ol>
*
* @param data
* @param comparator
* @param averageCalculator
* @return the median
*/
public static <T> T calculateMedian(final List<T> data,
final Comparator<? super T> comparator,
final AverageCalculator<T> averageCalculator){
final List<T> copy = new ArrayList<T>(data);
Collections.sort(copy, comparator);
return doCalculateMedian(data, averageCalculator);
}
/**
* Calculate the median of a supplied list.
* <ol>
* <li>A copy will be generated</li>
* <li>this copy will be sorted with the supplied comparator</li>
* <li>the median will be calculated, using the {@link #ALWAYS_FIRST} {@link AverageCalculator}
* for collections with an even number of items</li>
* </ol>
*
* @param data
* @param comparator
* @return the median
*/
@SuppressWarnings("unchecked")
public static <T> T calculateMedian(final List<T> data,
final Comparator<? super T> comparator){
return calculateMedian(data, comparator, (AverageCalculator<T>) ALWAYS_FIRST);
}
/**
* Calculate the median of a supplied list.
* <ol>
* <li>A copy will be generated</li>
* <li>this copy will be sorted using natural ordering</li>
* <li>the median will be calculated, using the {@link #ALWAYS_FIRST} {@link AverageCalculator}
* for collections with an even number of items</li>
* </ol>
*
* @param data
* @return the median
*/
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> T calculateMedian(final List<T> data){
return calculateMedian(data, (AverageCalculator<T>) ALWAYS_FIRST);
}
/**
* Calculate the median of a supplied list.
* <ol>
* <li>A copy will be generated</li>
* <li>this copy will be sorted using natural ordering</li>
* <li>the median will be calculated, using the supplied averageCalculator
* for collections with an even number of items</li>
* </ol>
*
* @param data
* @param averageCalculator
* @return the median
*/
public static <T extends Comparable<? super T>> T calculateMedian(final List<T> data,
final AverageCalculator<T> averageCalculator){
final List<T> copy = new ArrayList<T>(data);
Collections.sort(copy);
return doCalculateMedian(copy, averageCalculator);
}
private static <T> T doCalculateMedian(final List<T> sortedData,
final AverageCalculator<T> averageCalculator){
T result;
if(sortedData.isEmpty()){
result = null;
} else{
final int size = sortedData.size();
if(size % 2 == 0){
result =
averageCalculator.getAverage(sortedData.get(size / 2 - 1),
sortedData.get(size / 2));
} else{
result = sortedData.get(size / 2 - 1);
}
}
return result;
}
/**
* Generic accessor method for {@link #ALWAYS_FIRST}.
*/
@SuppressWarnings("unchecked")
public static <T> AverageCalculator<T> alwaysFirst(){
return ALWAYS_FIRST;
}
/**
* {@link AverageCalculator} implementation that always returns the lower
* bound unchanged.
*/
@SuppressWarnings("rawtypes")
public static final AverageCalculator ALWAYS_FIRST =
new AverageCalculator(){
@Override
public Object getAverage(final Object first, final Object second){
return first;
}
};
/**
* When there is an even number of items, this interface is used to generate
* the average between the two middle items.
*/
public static interface AverageCalculator<E> {
E getAverage(E first, E second);
}
}