blob: daa28e29ca9569c079caabcf0eabbbb26daea69d [file] [log] [blame]
/*
* 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.commons.functor.aggregator.functions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.functor.Function;
/**
* Aggregator function to be used with subclasses of
* {@link org.apache.commons.functor.aggregator.AbstractListBackedAggregator}
* which computes the <a href="http://en.wikipedia.org/wiki/Median">median</a>
* of all the numbers in the list.
*/
public final class IntegerMedianValueAggregatorFunction implements Function<List<Integer>, Integer> {
/**
* Flag to indicate whether we are going to operate on a copy of the list
* given or not. In order to compute the median, we need to sort the list
* first (and then choose the item in the middle). This function offers 2
* ways of doing the sorting:
* <ul>
* <li>by sorting (modifying) the original list (<code>useCopy=false</code>)
* </li>
* <li>by operating on a copy of the original list and leaving the original
* untouched (<code>useCopy=true</code>)</li>
* </ul>
* NOTE: While using a copy ensures the original list is untouched, it does
* mean we are creating a temporary list for the purpose of this computation
* so it will have an impact on memory!
*/
private boolean useCopy;
/**
* By default create a function which will operate on a copy of the original
* list ({@link #useCopy} = true).
*
* @see #useCopy
*/
public IntegerMedianValueAggregatorFunction() {
this(true);
}
/**
* Constructor which allows the caller to specify whether to operate on the
* original list or a copy of it.
*
* @param useCopy
* Set to true to operate on a copy of the list or false to
* operate on the original list.
* @see #useCopy
*/
public IntegerMedianValueAggregatorFunction(boolean useCopy) {
this.useCopy = useCopy;
}
/**
* Getter for {@link #useCopy}.
*
* @return Current value of {@link #useCopy}.
* @see #useCopy
*/
public boolean isUseCopy() {
return useCopy;
}
/**
* Sorts the given list and chooses the median value. The sorting can be
* carried out against the original list or a copy of it, based on the value
* of {@link #useCopy}.
*
* @param data
* List to compute the median value for
* @return the median value of the given list or <code>null</code> if the
* list is <code>null</code> or empty.
*/
public Integer evaluate(List<Integer> data) {
if (data == null || data.size() == 0) {
return null;
}
// if only one element in it, it is the mean
if (data.size() == 1) {
return data.get(0);
}
List<Integer> copy = data;
if (useCopy) {
copy = new ArrayList<Integer>(data);
}
Collections.sort(copy);
int n = copy.size();
int middle = n / 2;
if (n % 2 == 0) {
// need to compute the mean of middle and middle-1 (zero based
// index!)
return (copy.get(middle) + copy.get(middle - 1)) / 2;
}
// we're already positioned on the element in the middle so just return
// it
return copy.get(middle);
}
@Override
public String toString() {
return IntegerMedianValueAggregatorFunction.class.getName();
}
}