[LANG-723] Add mode and median Comparable... methods to ObjectUtils
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1147522 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
aee1b76fb6
commit
81b5435a3a
|
@ -20,8 +20,14 @@ import java.io.Serializable;
|
|||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.commons.lang3.exception.CloneFailedException;
|
||||
import org.apache.commons.lang3.mutable.MutableInt;
|
||||
|
||||
/**
|
||||
* <p>Operations on {@code Object}.</p>
|
||||
|
@ -410,6 +416,82 @@ public class ObjectUtils {
|
|||
return c1.compareTo(c2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "best guess" middle value among comparables. If there is an even
|
||||
* number of total values, the lower of the two middle values will be returned.
|
||||
* @param <T> type of values processed by this method
|
||||
* @param items to compare
|
||||
* @return T at middle position
|
||||
* @throws NullPointerException if items is {@code null}
|
||||
* @throws IllegalArgumentException if items is empty or contains {@code null} values
|
||||
*/
|
||||
public static <T extends Comparable<? super T>> T median(T... items) {
|
||||
Validate.notEmpty(items);
|
||||
Validate.noNullElements(items);
|
||||
TreeSet<T> sort = new TreeSet<T>();
|
||||
Collections.addAll(sort, items);
|
||||
@SuppressWarnings("unchecked") //we know all items added were T instances
|
||||
T result = (T) sort.toArray()[(sort.size() - 1) / 2];
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the "best guess" middle value among comparables. If there is an even
|
||||
* number of total values, the lower of the two middle values will be returned.
|
||||
* @param <T> type of values processed by this method
|
||||
* @param comparator to use for comparisons
|
||||
* @param items to compare
|
||||
* @return T at middle position
|
||||
* @throws NullPointerException if items or comparator is {@code null}
|
||||
* @throws IllegalArgumentException if items is empty or contains {@code null} values
|
||||
*/
|
||||
public static <T> T median(Comparator<T> comparator, T... items) {
|
||||
Validate.notEmpty(items, "null/empty items");
|
||||
Validate.noNullElements(items);
|
||||
Validate.notNull(comparator, "null comparator");
|
||||
TreeSet<T> sort = new TreeSet<T>(comparator);
|
||||
Collections.addAll(sort, items);
|
||||
@SuppressWarnings("unchecked") //we know all items added were T instances
|
||||
T result = (T) sort.toArray()[(sort.size() - 1) / 2];
|
||||
return result;
|
||||
}
|
||||
|
||||
// Mode
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Find the most frequently occurring item.
|
||||
*
|
||||
* @param <T>
|
||||
* @param items to check
|
||||
* @return most populous T, {@code null} if non-unique or no items supplied
|
||||
*/
|
||||
public static <T> T mode(T... items) {
|
||||
if (ArrayUtils.isNotEmpty(items)) {
|
||||
HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(items.length);
|
||||
for (T t : items) {
|
||||
MutableInt count = occurrences.get(t);
|
||||
if (count == null) {
|
||||
occurrences.put(t, new MutableInt(1));
|
||||
} else {
|
||||
count.increment();
|
||||
}
|
||||
}
|
||||
T result = null;
|
||||
int max = 0;
|
||||
for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) {
|
||||
int cmp = e.getValue().intValue();
|
||||
if (cmp == max) {
|
||||
result = null;
|
||||
} else if (cmp > max) {
|
||||
max = cmp;
|
||||
result = e.getKey();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// cloning
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.lang.reflect.Modifier;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -288,6 +289,75 @@ public class ObjectUtilsTest {
|
|||
assertEquals("one two true", -1, ObjectUtils.compare(one, two, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMedian() {
|
||||
assertEquals("foo", ObjectUtils.median("foo"));
|
||||
assertEquals("bar", ObjectUtils.median("foo", "bar"));
|
||||
assertEquals("baz", ObjectUtils.median("foo", "bar", "baz"));
|
||||
assertEquals("baz", ObjectUtils.median("foo", "bar", "baz", "blah"));
|
||||
assertEquals("blah", ObjectUtils.median("foo", "bar", "baz", "blah", "wah"));
|
||||
assertEquals(Integer.valueOf(5),
|
||||
ObjectUtils.median(Integer.valueOf(1), Integer.valueOf(5), Integer.valueOf(10)));
|
||||
assertEquals(
|
||||
Integer.valueOf(7),
|
||||
ObjectUtils.median(Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8),
|
||||
Integer.valueOf(9)));
|
||||
assertEquals(Integer.valueOf(6),
|
||||
ObjectUtils.median(Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8)));
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testMedian_nullItems() {
|
||||
ObjectUtils.median((String[]) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testMedian_emptyItems() {
|
||||
ObjectUtils.<String> median();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testComparatorMedian() {
|
||||
CharSequenceComparator cmp = new CharSequenceComparator();
|
||||
NonComparableCharSequence foo = new NonComparableCharSequence("foo");
|
||||
NonComparableCharSequence bar = new NonComparableCharSequence("bar");
|
||||
NonComparableCharSequence baz = new NonComparableCharSequence("baz");
|
||||
NonComparableCharSequence blah = new NonComparableCharSequence("blah");
|
||||
NonComparableCharSequence wah = new NonComparableCharSequence("wah");
|
||||
assertSame(foo, ObjectUtils.median(cmp, foo));
|
||||
assertSame(bar, ObjectUtils.median(cmp, foo, bar));
|
||||
assertSame(baz, ObjectUtils.median(cmp, foo, bar, baz));
|
||||
assertSame(baz, ObjectUtils.median(cmp, foo, bar, baz, blah));
|
||||
assertSame(blah, ObjectUtils.median(cmp, foo, bar, baz, blah, wah));
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testComparatorMedian_nullComparator() {
|
||||
ObjectUtils.median((Comparator<CharSequence>) null, new NonComparableCharSequence("foo"));
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testComparatorMedian_nullItems() {
|
||||
ObjectUtils.median(new CharSequenceComparator(), (CharSequence[]) null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testComparatorMedian_emptyItems() {
|
||||
ObjectUtils.median(new CharSequenceComparator());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Test
|
||||
public void testMode() {
|
||||
assertNull(ObjectUtils.mode((Object[]) null));
|
||||
assertNull(ObjectUtils.mode());
|
||||
assertNull(ObjectUtils.mode("foo", "bar", "baz"));
|
||||
assertNull(ObjectUtils.mode("foo", "bar", "baz", "foo", "bar"));
|
||||
assertEquals("foo", ObjectUtils.mode("foo", "bar", "baz", "foo"));
|
||||
assertEquals(Integer.valueOf(9),
|
||||
ObjectUtils.mode("foo", "bar", "baz", Integer.valueOf(9), Integer.valueOf(10), Integer.valueOf(9)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests {@link ObjectUtils#clone(Object)} with a cloneable object.
|
||||
*/
|
||||
|
@ -396,4 +466,43 @@ public class ObjectUtilsTest {
|
|||
}
|
||||
}
|
||||
|
||||
static final class NonComparableCharSequence implements CharSequence {
|
||||
final String value;
|
||||
|
||||
/**
|
||||
* Create a new NonComparableCharSequence instance.
|
||||
*
|
||||
* @param value
|
||||
*/
|
||||
public NonComparableCharSequence(String value) {
|
||||
super();
|
||||
Validate.notNull(value);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public char charAt(int arg0) {
|
||||
return value.charAt(arg0);
|
||||
}
|
||||
|
||||
public int length() {
|
||||
return value.length();
|
||||
}
|
||||
|
||||
public CharSequence subSequence(int arg0, int arg1) {
|
||||
return value.subSequence(arg0, arg1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
static final class CharSequenceComparator implements Comparator<CharSequence> {
|
||||
|
||||
public int compare(CharSequence o1, CharSequence o2) {
|
||||
return o1.toString().compareTo(o2.toString());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue