Abusing generics to implement a curried composition function in Java - java

So, after playing around with Java generics a bit, to get a deeper understanding of their capabilities, I decided to try to implement the curried version of the composition function, familiar to functional programmers. Compose has the type (in functional languages) (b -> c) -> (a -> b) -> (a -> c). Doing currying arithmetic functions wasn't too hard, since they are just polymorphic, but compose is a higher order function, and its proven taxing to my understanding of generics in Java.
Here is the implementation I've created so far:
public class Currying {
public static void main(String[] argv){
// Basic usage of currying
System.out.println(add().ap(3).ap(4));
// Next, lets try (3 * 4) + 2
// First lets create the (+2) function...
Fn<Integer, Integer> plus2 = add().ap(2);
// next, the times 3 function
Fn<Integer, Integer> times3 = mult().ap(3);
// now we compose them into a multiply by 2 and add 3 function
Fn<Integer, Integer> times3plus2 = compose().ap(plus2).ap(times3);
// now we can put in the final argument and print the result
// without compose:
System.out.println(plus2.ap(times3.ap(4)));
// with compose:
System.out.println(times3plus2.ap(new Integer(4)));
}
public static <A,B,C>
Fn<Fn<B,C>, // (b -> c) -> -- f
Fn<Fn<A,B>, // (a -> b) -> -- g
Fn<A,C>>> // (a -> c)
compose(){
return new Fn<Fn<B,C>,
Fn<Fn<A,B>,
Fn<A,C>>> () {
public Fn<Fn<A,B>,
Fn<A,C>> ap(final Fn<B,C> f){
return new Fn<Fn<A,B>,
Fn<A,C>>() {
public Fn<A,C> ap(final Fn<A,B> g){
return new Fn<A,C>(){
public C ap(final A a){
return f.ap(g.ap(a));
}
};
}
};
}
};
}
// curried addition
public static Fn<Integer, Fn<Integer, Integer>> add(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a + b;
}
};
}
};
}
// curried multiplication
public static Fn<Integer, Fn<Integer, Integer>> mult(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a * b;
}
};
}
};
}
}
interface Fn<A, B> {
public B ap(final A a);
}
The implementations of add, mult, and compose all compile just fine, but I find myself having a problem when it comes to actually using compose. I get the following error for line 12 (the first usage of compose in main):
Currying.java:12: ap(Fn<java.lang.Object,java.lang.Object>) in
Fn<Fn<java.lang.Object,java.lang.Object>,Fn<Fn<java.lang.Object,java.lang.Object>,Fn<java.lang.Object,java.lang.Object>>>
cannot be applied to (Fn<java.lang.Integer,java.lang.Integer>)
Fn<Integer,Integer> times3plus2 = compose().ap(plus2).ap(times3);
I assume this error is because generic types are invariant, but I am not sure how to solve the problem. From what I've read, wildcard type variables can be used to alleviate invariance in some cases, but I'm not sure how to use it here or even whether it will be useful.
Disclaimer: I have no intention of writing code like this in any real project. This is a fun "can it be done" kind of thing. Also, I made the variable names brief in defiance of standard Java practice because otherwise this example becomes an even more of an incomprehensible wall of text.

The basic problem here is that in the original call to compose(), there is no way for the compiler to infer the bindings of A, B, and C, so it assumes them all to be Object. You can fix it by specifying the type bindings explicitly:
Fn<Integer, Integer> times3plus2 =
Currying.<Integer, Integer, Integer>compose().ap(plus2).ap(times3);
Of course, then you lose the clarity that comes from type inference. If you need type inference, you could define some intermediate classes to do the inferring:
public static ComposeStart compose() {
return new ComposeStart();
}
class ComposeStart {
public <B,C> ComposeContinuation<B,C> ap(Fn<B,C> f) {
return new ComposeContinuation<B, C>(f);
}
}
class ComposeContinuation<B, C> {
private final Fn<B,C> f;
ComposeContinuation(Fn<B,C> f) {
this.f = f;
}
public <A> Fn<A,C> ap(final Fn<A,B> g) {
return new Fn<A,C>() {
public C ap(A a) {
return f.ap(g.ap(a));
}
};
}
}
However, then the intermediate steps of currying are no longer Fns.

Thanks to Russell Zahniser's insight that I wasn't giving Java enough information to work with, I changed the layout a bit so that we instantiate a "Composer" object with the appropriate type variables filled in. Here is my current working solution:
interface Fn<A, B> {
public B ap(final A a);
}
public class Currying {
public static void main(String[] argv){
// Basic usage of currying
System.out.println(add().ap(3).ap(4));
// Next, lets try (3 * 4) + 2
// First lets create the (+2) function...
Fn<Integer, Integer> plus2 = add().ap(2);
// next, the times 3 function
Fn<Integer, Integer> times3 = mult().ap(3);
// now we compose them into a multiply by 2 and add 3 function
Fn<Integer, Integer> times3plus2 = new Composer<Integer,Integer,Integer>()
.compose().ap(plus2).ap(times3);
// without compose
System.out.println(plus2.ap(times3.ap(4)));
// with compose
System.out.println(times3plus2.ap(4));
}
static class Composer<A,B,C> {
public
Fn<Fn<B,C>, // (b -> c) -> -- f
Fn<Fn<A,B>, // (a -> b) -> -- g
Fn<A,C>>> // (a -> c)
compose(){
return new Fn<Fn<B,C>,
Fn<Fn<A,B>,
Fn<A,C>>> () {
public Fn<Fn<A,B>,
Fn<A,C>> ap(final Fn<B,C> f){
return new Fn<Fn<A,B>,
Fn<A,C>>() {
public Fn<A,C> ap(final Fn<A,B> g){
return new Fn<A,C>(){
public C ap(final A a){
return f.ap(g.ap(a));
}
};
}
};
}
};
}
}
public static Fn<Integer, Fn<Integer, Integer>> add(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a + b;
}
};
}
};
}
public static Fn<Integer, Fn<Integer, Integer>> mult(){
return new Fn<Integer, Fn<Integer, Integer>>(){
public Fn<Integer,Integer> ap(final Integer a) {
return new Fn<Integer, Integer>() {
public Integer ap(final Integer b){
return a * b;
}
};
}
};
}
}

I've implemented this functionality myself in terms of a generic length-n chain of function calls.
public static final <X, Y> Chainer<X, Y> chain(
final Function<X, Y> primary
) {
return new Chainer<X, Y>(primary);
}
private static final class FunctionChain<IN, OUT> implements Function<IN, OUT> {
#SuppressWarnings("rawtypes")
private final List<Function> chain = new LinkedList<Function>();
private FunctionChain(#SuppressWarnings("rawtypes") final List<Function> chain) {
this.chain.addAll(chain);
}
#SuppressWarnings("unchecked")
#Override
public OUT apply(final IN in) {
Object ret = in;
for (final Function<Object, Object> f : chain) {
ret = f.apply(ret);
}
return (OUT) ret;
}
}
public static final class Chainer<IN, OUT> {
#SuppressWarnings("rawtypes")
private final LinkedList<Function> functions = new LinkedList<Function>();
#SuppressWarnings("unchecked")
private Chainer(#SuppressWarnings("rawtypes") final Function func) {
then(func);
}
#SuppressWarnings("unchecked")
public <OUT2> Chainer<IN, OUT2> then(final Function<OUT, OUT2> func) {
if (func instanceof FunctionChain) {
functions.addAll(((FunctionChain<?, ?>)func).chain);
} else {
functions.add(func);
}
return (Chainer<IN, OUT2>) this;
}
#SuppressWarnings("unchecked")
public Function<IN, OUT> build() {
// If empty, it's a noop function. If one element, there's no need for a chain.
return new FunctionChain<IN, OUT>(functions);
}
}
public static final <X, Y, Z> Function<X, Z> combine(
final Function<X, Y> primary,
final Function<Y, Z> secondary
) {
return chain(primary).then(secondary).build();
}
I would contend that this qualifies as a greater abuse of generics, since the Chainer class only uses generics to ensure that subsequent .then() calls are properly typed based on the last function supplied, and the functions are just stored in a list in what is known to be a safe calling order for later, but it does work and it does generalize well: chain(first).then(second).then(third).then(fourth).build() is completely valid with this approach.
Just be be explicit, this is based around Function from guava, but should port over to any Function interface just fine.

Related

binarySearch with generics and capture of

I have a Task:
public interface Task {
}
Then I have implementations for those:
public interface Task__Init extends Task {
void init(Element e);
}
public interface Task__Hit_Test extends Task {
boolean hit_test(Element e, float x, float y);
}
public interface Task__Draw extends Task {
void draw(Element e);
}
I also have a class that can hold a instance of those implementations:
static public class Task_Holder<T extends Task> {
public int task_id;
public Task_Type type;
public T task;
// ...
}
Then I have a class that holds those, where the last ArrayList holds all of them (all_task_holders)
static public class Implementation_Context {
public HashMap<String, ArrayList<Task_Holder<Task__Init>>> init_solvers = new HashMap<>();
public HashMap<String, ArrayList<Task_Holder<Task__Draw>>> draw_solvers = new HashMap<>();
public HashMap<String, ArrayList<Task_Holder<Task__Hit_Test>>> hit_test_solvers = new HashMap<>();
public ArrayList<Task_Holder<? extends Task>> all_task_holders = new ArrayList<>();
// ...
}
Now one of the problem arises:
static public Task_Holder<?> find_task_holder(int task_id) {
Comparator<Task_Holder<?>> comparator = (a, b)-> {
if (a.task_id < b.task_id) return -1;
if (a.task_id > b.task_id) return 1;
return 0;
};
Collections.sort(ctx.implementation.all_task_holders, comparator);
Task_Holder<?> key = new Task_Holder<>();
key.task_id = task_id;
int index = Collections.binarySearch(ctx.implementation.all_task_holders, key);
for (Task_Holder<?> th : ctx.implementation.all_task_holders) {
if (th.task_id == task_id) {
return th;
}
}
assert false; // should we find things that are not there?
return null;
}
For the binarySearch I get (I make it a codeblock here, else stackoverflow removes words for some reason?):
The method binarySearch(List<? extends Comparable<? super T>>,
T) in the type Collections is not applicable for the arguments
(ArrayList<sfjl_ui.Task_Holder<?>>, sfjl_ui.Task_Holder<capture#6-of
?>)
I have no clue how to fix this. Every attempt breaks other things (for example I break the sort that's 3 lines higher).
It feels like paying off credit card debts with other credit cards, you never win.
How can I fix this?
Pass the comparator as an additional argument:
int index = Collections.binarySearch(ctx.all_task_holders, key, comparator);

Remove duplicated code from same method in different objects

I have 2 different objects, Parameter and Variable which are fields of Context.
I have also third object: ParameterBase which is not part of Context.
All 3 classes are auto-generated, I can't modify them.
All 3 classes have 2 fields: name and value which are of type String.
All 3 classes have getters and setters.
I implemented the following methods:
public static List<ParametersBase> removeDuplicate(List<ParametersBase> parameterList) {
return new ArrayList<>(parameterList.stream().collect(
Collectors.toMap(
ParametersBase::getParamName,
Function.identity(),
Utils::mergeDuplicate
)
).values());
}
private static ParametersBase mergeDuplicate(ParametersBase a, ParametersBase b) {
if (a.getParamValue().equals(b.getParamValue())) {
return a;
} else {
throw new IllegalArgumentException("Error message");
}
}
I want to do use this methods for the other 2 classes, so I did that:
private static void removeDuplicatesParams(Context context) {
final List<ParametersBase> parameterList = emptyIfNull(
context.getParameters()).stream()
.map(parameter -> new ParametersBase()
.paramName(parameter.getParamName())
.paramValue(parameter.getParamValue()))
.collect(Collectors.toList());
List<ParametersBase> distinctParameterList = removeDuplicate(parameterList);
List<Parameter> distinctParametersList = distinctParameterList
.stream().map(temp -> new Parameter().paramName(temp.getParamName())
.paramValue(temp.getParamValue())).collect(Collectors.toList());
context.setParameters(distinctParametersList);
}
private static void removeDuplicatesVariables(Context context) {
final List<ParametersBase> parameterList = emptyIfNull(
context.getVariables()).stream()
.map(parameter -> new ParametersBase()
.paramName(parameter.getParamName())
.paramValue(parameter.getParamValue()))
.collect(Collectors.toList());
List<ParametersBase> distinctParameterList = removeDuplicate(parameterList);
List<Variable> distinctParametersList = distinctParameterList
.stream().map(temp -> new Variable().paramName(temp.getParamName())
.paramValue(temp.getParamValue())).collect(Collectors.toList());
context.setVariables(distinctParametersList);
}
As you see, the two methods which I created are almost the same, but since I have 2 auto-generated classes I have to duplicate the code. Is there a way to make the code prettier? I'm using Java 8.
You can generify removeDuplicate method:
private static <T> List<T> removeDuplicate(
Collection<? extends T> parameterList,
Function<? super T, String> name,
Function<? super T, String> value
) {
return new ArrayList<>(parameterList.stream().collect(
Collectors.<T, String, T>toMap(
name,
Function.identity(),
(a, b) -> mergeDuplicate(a, b, value)
)).values()
);
}
private static <T> T mergeDuplicate(T a, T b, Function<? super T, String> value) {
if (value.apply(a).equals(value.apply(b))) return a;
else throw new IllegalArgumentException("Error message");
}
And use it like this:
private static void removeDuplicatesParams(Context context) {
context.setParameters(removeDuplicate(
context.getParameters(),
Parameter::getParamName,
Parameter::getParamValue
));
}
Or you can apply the adapter design pattern:
private interface ParameterAdapter<T> {
T original();
String name();
String value();
}
private static <T> List<T> removeDuplicate(
Collection<? extends T> parameterList,
Function<? super T, ? extends ParameterAdapter<T>> adapter
) {
return parameterList.stream()
.<ParameterAdapter<T>>map(adapter)
.collect(Collectors.toMap(
ParameterAdapter::name,
Function.identity(),
Utils::mergeDuplicate
)).values().stream()
.map(ParameterAdapter::original)
.collect(Collectors.toList());
}
private static <T> ParameterAdapter<T> mergeDuplicate(
ParameterAdapter<T> a,
ParameterAdapter<T> b
) {
if (a.value().equals(b.value())) return a;
else throw new IllegalArgumentException("Error message");
}
And use it like this:
private static void removeDuplicatesParams(Context context) {
context.setParameters(removeDuplicate(
context.getParameters(),
Utils::adaptParameter
));
}
private static ParameterAdapter<Parameter> adaptParameter(Parameter parameter) {
return new ParameterAdapter<Parameter>() {
#Override
public Parameter original() { return parameter; }
#Override
public String name() { return parameter.getParamName(); }
#Override
public String value() { return parameter.getParamValue(); }
};
}

Custom Collector for Collectors.groupingBy doesn't work as expected

Consider the simple class Foo:
public class Foo {
public Float v1;
public Float v2;
public String name;
public Foo(String name, Float v1, Float v2) {
this.name = name;
this.v1 = v1;
this.v2 = v2;
}
public String getName() {
return name;
}
}
Now, I have a collection of Foos and I'd like to group them by Foo::getName. I wrote a custom Collector to do that but it doesn't seem to work as expected. More precisely, combiner() never gets called. Why?
public class Main {
public static void main(String[] args) {
List<Foo> foos = new ArrayList<>();
foos.add(new Foo("blue", 2f, 2f));
foos.add(new Foo("blue", 2f, 3f));
foos.add(new Foo("green", 3f, 4f));
Map<String, Float> fooGroups = foos.stream().collect(Collectors.groupingBy(Foo::getName, new FooCollector()));
System.out.println(fooGroups);
}
private static class FooCollector implements Collector<Foo, Float, Float> {
#Override
public Supplier<Float> supplier() {
return () -> new Float(0);
}
#Override
public BiConsumer<Float, Foo> accumulator() {
return (v, foo) -> v += foo.v1 * foo.v2;
}
#Override
public BinaryOperator<Float> combiner() {
return (v1, v2) -> v1 + v2;
}
#Override
public Function<Float, Float> finisher() {
return Function.identity();
}
#Override
public Set<Characteristics> characteristics() {
Set<Characteristics> characteristics = new TreeSet<>();
return characteristics;
}
}
}
First, the combiner function does not need to get called if you aren't using multiple threads (parallel stream). The combiner gets called to combine the results of the operation on chunks of your stream. There is no parallelism here so the combiner doesn't need to be called.
You are getting zero values because of your accumulator function. The expression
v += foo.v1 * foo.v2;
will replace v with a new Float object. The original accumulator object is not modified; it is still 0f. Besides, Float, like other numeric wrapper types (and String) is immutable and cannot be changed.
You need some other kind of accumulator object that is mutable.
class FloatAcc {
private Float total;
public FloatAcc(Float initial) {
total = initial;
}
public void accumulate(Float item) {
total += item;
}
public Float get() {
return total;
}
}
Then you can modify your custom Collector to use FloatAcc. Supply a new FloatAcc, call accumulate in the accumulator function, etc.
class FooCollector implements Collector<Foo, FloatAcc, Float> {
#Override
public Supplier<FloatAcc> supplier() {
return () -> new FloatAcc(0f);
}
#Override
public BiConsumer<FloatAcc, Foo> accumulator() {
return (v, foo) -> v.accumulate(foo.v1 * foo.v2);
}
#Override
public BinaryOperator<FloatAcc> combiner() {
return (v1, v2) -> {
v1.accumulate(v2.get());
return v1;
};
}
#Override
public Function<FloatAcc, Float> finisher() {
return FloatAcc::get;
}
#Override
public Set<Characteristics> characteristics() {
Set<Characteristics> characteristics = new TreeSet<>();
return characteristics;
}
}
With these changes I get what you're expecting:
{green=12.0, blue=10.0}
You have an explanation as to why the current collector does not work from rgettman.
It is worth checking to see what helper methods exist to create custom collectors. For example, this entire collector can be defined far more concisely as:
reducing(0.f, v -> v.v1 * v.v2, (a, b) -> a + b)
It is not always possible to use methods like these; but the conciseness (and, presumably, the well-testedness) should make them the first choice when possible.

Tree traversal in Java with Generic classes

To be precise, I am trying to flatten a tree and I am stuck on trying to get the values of private attributes in a generic class using a generic function.
I have attached the classes to show how the tree is structured exactly. But it's looks something like this:
/|\
1 | 6
/|\
5 4 9
I am going to paste my attempt at the end. First, let me introduce the classes:
Triple simply stores three values of the same type.
public class Triple<V> {
private final V l, m, r;
public Triple(V l, V m, V r) {
this.l = l;
this.m = m;
this.r = r;
}
public V left() { return l; }
public V middle() { return m; }
public V right() { return r; }
}
Straightforward interface:
public interface Function<P, R> {
R apply(P p);
}
Now, for a tricky class. This one is simply a type that stores one of EitherOr of two types of value, but not both.
public class EitherOr<A,B> {
// Constructs a left-type EitherOr
public static <A> EitherOr left(A a) {
return new EitherOr(a, null);
}
// Constructs a right-type EitherOr
public static <B> EitherOr right(B b) {
return new EitherOr(null, b);
}
private final A a;
private final B b;
private EitherOr(A a, B b) {
this.a = a; this.b = b;
}
public<T> T ifLeft(Function<A,T> f) {
return f.apply(a);
}
public<T> T ifRight(Function<B,T> f) {
return f.apply(b);
}
public boolean isLeft() {
return b == null;
}
}
I know this is getting long, but bear with me. This class implements the tree structure.
public interface Tree<T> {
EitherOr<T, Triple<Tree<T>>> get();
static final class Leaf<T> implements Tree<T> {
public static <T> Leaf<T> leaf (T value) {
return new Leaf<T>(value);
}
private final T t;
public Leaf(T t) { this.t = t; }
#Override
public EitherOr<T, Triple<Tree<T>>> get() {
return EitherOr.left(t);
}
}
static final class Node<T> implements Tree<T> {
public static <T> Tree<T> tree (T left, T middle, T right) {
return new Node<T>(Leaf.leaf(left), Leaf.leaf(middle), Leaf.leaf(right));
}
private final Triple<Tree<T>> branches;
public Node(Tree<T> left, Tree<T> middle, Tree<T> right) {
this.branches = new Triple<Tree<T>>(left, middle, right);
}
#Override
public EitherOr<T, Triple<Tree<T>>> get() {
return EitherOr.right(branches);
}
}
}
Alright. Here is my idea for flattening:
public class MyFlattenTree<T> implements FlattenTree<T> {
public List<T> flattenInOrder(Tree<T> tree) {
List<T> list = new ArrayList<T>();
EitherOr<T, Triple<Tree<T>>> EitherOr;
EitherOr = tree.get();
// it is a leaf
if (EitherOr.isLeft()) {
// This is where the problem lies
// I don't how to get the value using a function f
list.add((T) EitherOr.ifLeft(f));
return list;
}
else {
// basically recursively go through the tree somehow
}
return null;
}
}
As I said, I am stuck with trying to retreive the value in the EitherOr class using the Function interface. I am thinking of implementing the Function interface and write a function for "apply" that just gets the value, but I am not sure how to do that. Any help would be appreciated. Thanks!
So, here is your flattenInOrder method:
public List<T> flattenInOrder(final Tree<T> tree) {
final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
if (EitherOr.isLeft()) {
return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
}
return EitherOr.ifRight(this.ifRightFunction);
}
Quite simple, assuming that:
ifLeftFunction yields a single element (since EitherOr<T, Triple<Tree<T>>> has a single T elem' if it s "left")
... and:
ifRightFunction yields a collection of elements (since EitherOr<T, Triple<Tree<T>>> has a list of T elems' if it is "right")
Let's look into these functions now:
ifLeftFunction is... basic. I want to extract a T from... a T.
final Function<T, T> ifLeftFunction = new Function<T, T>() {
#Override
public T apply(final T t) {
return t;
}
};
ifRightFunction is slightly more complex: it has to be recursive and collect all Ts from the Tree it's browsing:
final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {
#Override
public List<T> apply(final Triple<Tree<T>> t) {
final List<T> res = new ArrayList<>();
res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
return res;
}
};
And... you're done!
Sample working code:
public class MyFlattenTree<T> {
private final Function<Triple<Tree<T>>, List<T>> ifRightFunction = new Function<Triple<Tree<T>>, List<T>>() {
#Override
public List<T> apply(final Triple<Tree<T>> t) {
final List<T> res = new ArrayList<>();
res.addAll(MyFlattenTree.this.flattenInOrder(t.left()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.middle()));
res.addAll(MyFlattenTree.this.flattenInOrder(t.right()));
return res;
}
};
private final Function<T, T> ifLeftFunction = new Function<T, T>() {
#Override
public T apply(final T t) {
return t;
}
};
public static void main(final String[] args) {
final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
}
public List<T> flattenInOrder(final Tree<T> tree) {
final EitherOr<T, Triple<Tree<T>>> EitherOr = tree.get();
if (EitherOr.isLeft()) {
return Collections.singletonList(EitherOr.ifLeft(this.ifLeftFunction));
}
return EitherOr.ifRight(this.ifRightFunction);
}
}
Note that I'm creating the exact Tree you're featuring as an example in your question in the main method here:
public static void main(final String[] args) {
final Tree<String> tree = new Node<>(new Leaf<>("1"), new Node<>(new Leaf<>("5"), new Leaf<>("4"), new Leaf<>("9")), new Leaf<>("6"));
System.out.println(new MyFlattenTree<String>().flattenInOrder(tree));
}
Output: [1, 5, 4, 9, 6]
Cheers ;)

How do I write a method which would work on both lists and arrays?

I have a method which looks like this:
void foo (List<String> list, ...) {
...
for (String s : list) { // this is the only place where `list` is used
...
}
...
}
the exact same code would work if I replace List<String> list with String[] list, however, to avoid spaghetti code, I keep the single method, and when I need to call it on an array a, I do it like this: foo(Arrays.asList(a)).
I wonder if this is The Right Way.
Specifically,
What is the overhead of Arrays.asList()?
Is there a way to write a method which would accept both arrays and lists, just like the for loop does?
Thanks!
Arrays.asList() has a small overhead. There is no real way to implement one method for both List and arrays.
But you can do the following:
void foo (List<String> list, ...) {
...
for (String s : list) { // this is the only place where *list* is used
...
}
...
}
void foo (String[] arr, ...) {
if ( arr != null ) {
foo(Arrays.asList(arr),...);
}
}
From the source code of openjdk, Arrays.asList:
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
furthermore:
ArrayList(E[] array) {
if (array==null)
throw new NullPointerException();
a = array;
}
So basically all that happens in an assignment, so the overhead should be negligible.
The overhead is that it converts an array to a list--how it does so would be implementation-dependent, it only needs to fulfill the contract.
IMO you should write two methods if you're concerned about the potential runtime overhead: that is the nature of Java; methods have type signatures, and they must be obeyed.
Do avoid this I just use and allow Lists, Sets and Maps (like Joshua Bloch told us). There is no way to merge both "collection types".
An alternative is to use guava (Iterators/Iteratables). So you can iterarte over your collections without a deep copy of them.
Good question.
This is a very common case, and is often dealt with by writing two separate methods. However code duplication is really a bad idea, and whenever you find yourself duplicating code, you should start looking for opportunities to factor your code better. (As you are doing right now!)
Now if you look into the source of java.util.Arrays, you will notice that Arrays.asList retruns an instance of a private inner class Arrays.ArrayList which is just a thin wrapper over plain arrays, and delegates all relevant method calls to it. (This is known as a projection or view of a data structure.) Therefore the overhead incurred is insignificant (unless you are striving to extract every last bit of performance), and in my opinion, you should go ahead and use this method without worrying about performance.
The solution I personally use is as follows.
I have a class named RichIterable in my personal utils. As the name indicates the class wraps over Iterable and provides some additional useful methods not already present. The class also has a factory method that creates an RichIterable from an array. Here is the class definition.
public class RichIterable<A> implements Iterable<A> {
private Iterable<A> xs;
private RichIterable(Iterable<A> xs) {
this.xs = xs;
}
public static <A> RichIterable<A> from(Iterable<A> xs) {
if (xs instanceof RichIterable) {
return (RichIterable<A>) xs;
} else {
return new RichIterable<A>(xs);
}
}
public static <A> RichIterable<A> from(final Enumeration<A> xs) {
Iterable<A> iterable = new Iterable<A>() {
#Override
public Iterator<A> iterator() {
return new Iterator<A>() {
#Override
public boolean hasNext() {
return xs.hasMoreElements();
}
#Override
public A next() {
return xs.nextElement();
}
#Override
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove an element from an enumeration.");
}
};
}
};
return RichIterable.from(iterable);
}
public static <A> RichIterable<A> from(final A[] xs) {
Iterable<A> iterable = new Iterable<A>() {
#Override
public Iterator<A> iterator() {
return new Iterator<A>() {
private int i = 0;
#Override
public boolean hasNext() {
return i < xs.length;
}
#Override
public A next() {
A x = xs[i];
i++;
return x;
}
#Override
public void remove() {
throw new UnsupportedOperationException(
"Cannot remove an element from an array.");
}
};
}
};
return RichIterable.from(iterable);
}
public boolean isEmpty() {
if (xs instanceof Collection) {
return ((Collection) xs).isEmpty();
}
for (A x : xs) {
return false;
}
return true;
}
public int size() {
if (xs instanceof Collection) {
return ((Collection) xs).size();
}
int size = 0;
for (A x : xs) {
size++;
}
return size;
}
public ArrayList<A> toArrayList() {
ArrayList<A> ys = new ArrayList<A>();
for (A x : xs) {
ys.add(x);
}
return ys;
}
public <B> RichIterable<B> map(F1<A, B> f) {
List<B> ys = new ArrayList<B>();
for (A x : xs) {
ys.add(f.apply(x));
}
return RichIterable.from(ys);
}
public RichIterable<A> filter(F1<A, Boolean> pred) {
List<A> ys = new ArrayList<A>();
Arrays.asList();
for (A x : xs) {
if (pred.apply(x)) {
ys.add(x);
}
}
return RichIterable.from(ys);
}
public boolean exists(F1<A, Boolean> pred) {
for (A x : xs) {
if (pred.apply(x)) {
return true;
}
}
return false;
}
public boolean forall(F1<A, Boolean> pred) {
for (A x : xs) {
if (!pred.apply(x)) {
return false;
}
}
return true;
}
public Maybe<A> find(F1<A, Boolean> pred) {
for (A x : xs) {
if (pred.apply(x)) {
return Just.of(x);
}
}
return Nothing.value();
}
public String mkString(String beg, String sep, String end) {
Iterator<A> i = xs.iterator();
if (!i.hasNext()) {
return beg + end;
}
StringBuilder sb = new StringBuilder();
sb.append(beg);
while (true) {
A e = i.next();
sb.append(e.toString());
if (!i.hasNext()) {
return sb.append(end).toString();
}
sb.append(sep);
}
}
public String mkString(String sep) {
return mkString("", sep, "");
}
public String mkString() {
return this.mkString(", ");
}
public Iterable<A> getRaw() {
return xs;
}
#Override
public Iterator<A> iterator() {
return xs.iterator();
}
}

Categories