Python-like list comprehension in Java

JavaPythonParametersMethodsList Comprehension

Java Problem Overview


Since Java doesn't allow passing methods as parameters, what trick do you use to implement Python like list comprehension in Java ?

I have a list (ArrayList) of Strings. I need to transform each element by using a function so that I get another list. I have several functions which take a String as input and return another String as output. How do I make a generic method which can be given the list and the function as parameters so that I can get a list back with each element processed. It is not possible in the literal sense, but what trick should I use ?

The other option is to write a new function for each smaller String-processing function which simply loops over the entire list, which is kinda not so cool.

Java Solutions


Solution 1 - Java

In Java 8 you can use method references:

List<String> list = ...;
list.replaceAll(String::toUpperCase);

Or, if you want to create a new list instance:

List<String> upper = list.stream().map(String::toUpperCase).collect(Collectors.toList());

Solution 2 - Java

Basically, you create a Function interface:

public interface Func<In, Out> {
    public Out apply(In in);
}

and then pass in an anonymous subclass to your method.

Your method could either apply the function to each element in-place:

public static <T> void applyToListInPlace(List<T> list, Func<T, T> f) {
    ListIterator<T> itr = list.listIterator();
    while (itr.hasNext()) {
        T output = f.apply(itr.next());
        itr.set(output);
    }
}
// ...
List<String> myList = ...;
applyToListInPlace(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

or create a new List (basically creating a mapping from the input list to the output list):

public static <In, Out> List<Out> map(List<In> in, Func<In, Out> f) {
    List<Out> out = new ArrayList<Out>(in.size());
    for (In inObj : in) {
        out.add(f.apply(inObj));
    }
    return out;
}
// ...
List<String> myList = ...;
List<String> lowerCased = map(myList, new Func<String, String>() {
    public String apply(String in) {
        return in.toLowerCase();
    }
});

Which one is preferable depends on your use case. If your list is extremely large, the in-place solution may be the only viable one; if you wish to apply many different functions to the same original list to make many derivative lists, you will want the map version.

Solution 3 - Java

The Google Collections library has lots of classes for working with collections and iterators at a much higher level than plain Java supports, and in a functional manner (filter, map, fold, etc.). It defines Function and Predicate interfaces and methods that use them to process collections so that you don't have to. It also has convenience functions that make dealing with Java generics less arduous.

I also use Hamcrest** for filtering collections.

The two libraries are easy to combine with adapter classes.


** Declaration of interest: I co-wrote Hamcrest

Solution 4 - Java

Apache Commons CollectionsUtil.transform(Collection, Transformer) is another option.

Solution 5 - Java

I'm building this project to write list comprehension in Java, now is a proof of concept in https://github.com/farolfo/list-comprehension-in-java

Examples

// { x | x E {1,2,3,4} ^ x is even }
// gives {2,4}

Predicate<Integer> even = x -> x % 2 == 0;

List<Integer> evens = new ListComprehension<Integer>()
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// evens = {2,4};

And if we want to transform the output expression in some way like

// { x * 2 | x E {1,2,3,4} ^ x is even }
// gives {4,8}

List<Integer> duplicated = new ListComprehension<Integer>()
    .giveMeAll((Integer x) -> x * 2)
    .suchThat(x -> {
        x.belongsTo(Arrays.asList(1, 2, 3, 4));
        x.is(even);
    });
// duplicated = {4,8}

Solution 6 - Java

You can use lambdas for the function, like so:

class Comprehension<T> {
	/**
    *in: List int
    *func: Function to do to each entry
    */
	public List<T> comp(List<T> in, Function<T, T> func) {
		List<T> out = new ArrayList<T>();
		for(T o: in) {
			out.add(func.apply(o));
		}
		return out;
	}
}

the usage:

List<String> stuff = new ArrayList<String>();
stuff.add("a");
stuff.add("b");
stuff.add("c");
stuff.add("d");
stuff.add("cheese");
List<String> newStuff = new Comprehension<String>().comp(stuff, (a) -> { //The <String> tells the comprehension to return an ArrayList<String>
	a.equals("a")? "1": 
			(a.equals("b")? "2": 
				(a.equals("c")? "3":
					(a.equals("d")? "4": a
	)))
});

will return:

["1", "2", "3", "4", "cheese"]

Solution 7 - Java

import java.util.Arrays;

class Soft{
    public static void main(String[] args){
        int[] nums=range(9, 12);
        System.out.println(Arrays.toString(nums));
    }
    static int[] range(int low, int high){
        int[] a=new int[high-low];
        for(int i=0,j=low;i<high-low;i++,j++){
            a[i]=j;
        }
        return a;

    }
}

Hope, that I help you :)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questioneuphoria83View Question on Stackoverflow
Solution 1 - JavayurezView Answer on Stackoverflow
Solution 2 - JavaMichael MyersView Answer on Stackoverflow
Solution 3 - JavaNatView Answer on Stackoverflow
Solution 4 - Javauser111246View Answer on Stackoverflow
Solution 5 - JavafarolfoView Answer on Stackoverflow
Solution 6 - Javauser6144948View Answer on Stackoverflow
Solution 7 - JavaHayk EminyanView Answer on Stackoverflow