package com.loadimpact.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Utility methods for lists.
*
* @author jens
*/
public class ListUtils {
/**
* Call-back interface for {@link #map(java.util.List, com.loadimpact.util.ListUtils.MapClosure)}.
* @param <From> value type
* @param <To> destination type
*/
public interface MapClosure<From, To> {
To eval(From value);
}
/**
* Call-back interface for {@link #reduce(java.util.List, Object, com.loadimpact.util.ListUtils.ReduceClosure)}.
* @param <Accumulator> binary accumulator function
* @param <Value> element type
*/
public interface ReduceClosure<Accumulator, Value> {
Accumulator eval(Accumulator acc, Value value);
}
// /**
// * Concatenates a list of strings using the given separator.
// * @param lst list of text strings
// * @param separator separator
// * @return string
// */
// public static String join(Collection<String> lst, String separator) {
// StringBuilder buf = new StringBuilder(lst.size() * 64);
// boolean first = true;
// for (String value : lst) {
// if (first) first = false; else buf.append(separator);
// buf.append(value);
// }
// return buf.toString();
// }
/**
* Concatenates a list of objects using the given separator.
* @param lst list of text strings
* @param separator separator
* @return string
*/
public static String join(Collection<? extends Object> lst, String separator) {
StringBuilder buf = new StringBuilder(lst.size() * 64);
boolean first = true;
for (Object value : lst) {
if (first) first = false; else buf.append(separator);
buf.append(value.toString());
}
return buf.toString();
}
/**
* Returns the last element of a list, or null if empty.
* @param lst the list
* @param <T> element type
* @return last element or null
*/
public static <T> T last(List<T> lst) {
if (lst == null || lst.isEmpty()) return null;
return lst.get(lst.size() - 1);
}
/**
* Applies a function to every item in a list.
* @param list the list with values
* @param f closure to apply
* @param <From> value type
* @param <To> destination type
* @return list of transformed values
*/
@SuppressWarnings("unchecked")
public static <From, To> List<To> map(List<From> list, MapClosure<From,To> f) {
List<To> result = new ArrayList<To>(list.size());
for (From value : list) {
result.add( f.eval(value) );
}
return result;
}
/**
* Applies a binary function between each element of the given list.
* @param list list of elements
* @param init initial value for the accumulator
* @param f accumulator expression to apply
* @param <Accumulator> binary function
* @param <Value> element type
* @return an accumulated/aggregated value
*/
public static <Accumulator, Value> Accumulator reduce(List<Value> list, Accumulator init, ReduceClosure<Accumulator,Value> f) {
Accumulator accumulator = init;
for (Value value : list) {
accumulator = f.eval(accumulator, value);
}
return accumulator;
}
/**
* Computes the average/mean/expected value of a list of numbers.
* @param values list of values
* @return the computed average
*/
public static double average(List<? extends Number> values) {
if (values == null || values.isEmpty()) return 0D;
double sum = 0D;
for (Number v : values) sum += v.doubleValue();
return sum / values.size();
}
/**
* Computes the median value of a list of integers.
* @param values list of values
* @return the computed medium
*/
@SuppressWarnings("unchecked")
public static int median(List<Integer> values) {
if (values == null || values.isEmpty()) return 0;
values = new ArrayList<Integer>(values);
Collections.sort(values);
final int size = values.size();
final int sizeHalf = size / 2;
if (size % 2 == 1) { //is odd?
// 0 1 [2] 3 4: size/2 = 5/2 = 2.5 -> 2
return values.get(sizeHalf);
}
// 0 1 [2 3] 4 5: size/2 = 6/2 = 3
return (values.get(sizeHalf - 1) + values.get(sizeHalf)) / 2;
}
}