Author: mbenson
Date: Sun Jul 17 04:22:51 2011
New Revision: 1147522
URL: http://svn.apache.org/viewvc?rev=1147522&view=rev
Log:
[LANG-723] Add mode and median Comparable... methods to ObjectUtils
Modified:
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/ObjectUtils.java
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java
Modified:
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/ObjectUtils.java
URL:
http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/ObjectUtils.java?rev=1147522&r1=1147521&r2=1147522&view=diff
==============================================================================
---
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/ObjectUtils.java
(original)
+++
commons/proper/lang/trunk/src/main/java/org/apache/commons/lang3/ObjectUtils.java
Sun Jul 17 04:22:51 2011
@@ -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
//-----------------------------------------------------------------------
/**
Modified:
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java?rev=1147522&r1=1147521&r2=1147522&view=diff
==============================================================================
---
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java
(original)
+++
commons/proper/lang/trunk/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java
Sun Jul 17 04:22:51 2011
@@ -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());
+ }
+
+ }
}