001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.functor.aggregator.functions; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022 023import org.apache.commons.functor.UnaryFunction; 024 025/** 026 * Aggregator function to be used with subclasses of 027 * {@link org.apache.commons.functor.aggregator.AbstractListBackedAggregator} 028 * which computes the <a href="http://en.wikipedia.org/wiki/Median">median</a> 029 * of all the numbers in the list. 030 */ 031public final class IntegerMedianValueAggregatorFunction implements UnaryFunction<List<Integer>, Integer> { 032 /** 033 * Flag to indicate whether we are going to operate on a copy of the list 034 * given or not. In order to compute the median, we need to sort the list 035 * first (and then choose the item in the middle). This function offers 2 036 * ways of doing the sorting: 037 * <ul> 038 * <li>by sorting (modifying) the original list (<code>useCopy=false</code>) 039 * </li> 040 * <li>by operating on a copy of the original list and leaving the original 041 * untouched (<code>useCopy=true</code>)</li> 042 * </ul> 043 * NOTE: While using a copy ensures the original list is untouched, it does 044 * mean we are creating a temporary list for the purpose of this computation 045 * so it will have an impact on memory! 046 */ 047 private boolean useCopy; 048 049 /** 050 * By default create a function which will operate on a copy of the original 051 * list ({@link #useCopy} = true). 052 * 053 * @see #useCopy 054 */ 055 public IntegerMedianValueAggregatorFunction() { 056 this(true); 057 } 058 059 /** 060 * Constructor which allows the caller to specify whether to operate on the 061 * original list or a copy of it. 062 * 063 * @param useCopy 064 * Set to true to operate on a copy of the list or false to 065 * operate on the original list. 066 * @see #useCopy 067 */ 068 public IntegerMedianValueAggregatorFunction(boolean useCopy) { 069 this.useCopy = useCopy; 070 } 071 072 /** 073 * Getter for {@link #useCopy}. 074 * 075 * @return Current value of {@link #useCopy}. 076 * @see #useCopy 077 */ 078 public boolean isUseCopy() { 079 return useCopy; 080 } 081 082 /** 083 * Sorts the given list and chooses the median value. The sorting can be 084 * carried out against the original list or a copy of it, based on the value 085 * of {@link #useCopy}. 086 * 087 * @param data 088 * List to compute the median value for 089 * @return the median value of the given list or <code>null</code> if the 090 * list is <code>null</code> or empty. 091 */ 092 public Integer evaluate(List<Integer> data) { 093 if (data == null || data.size() == 0) { 094 return null; 095 } 096 // if only one element in it, it is the mean 097 if (data.size() == 1) { 098 return data.get(0); 099 } 100 List<Integer> copy = data; 101 if (useCopy) { 102 copy = new ArrayList<Integer>(data); 103 } 104 Collections.sort(copy); 105 int n = copy.size(); 106 int middle = n / 2; 107 if (n % 2 == 0) { 108 // need to compute the mean of middle and middle-1 (zero based 109 // index!) 110 return (copy.get(middle) + copy.get(middle - 1)) / 2; 111 } 112 113 // we're already positioned on the element in the middle so just return 114 // it 115 return copy.get(middle); 116 } 117 118 @Override 119 public String toString() { 120 return IntegerMedianValueAggregatorFunction.class.getName(); 121 } 122}