views:

168

answers:

5

Is there a construct in java that does something like this(here implemented in python):

[] = [item for item in oldList if item.getInt() > 5]

Today I'm using something like:

ItemType newList = new ArrayList();
for( ItemType item : oldList ) {
    if( item.getInt > 5) {
     newList.add(item);
    }
}

And to me the first way looks a bit smarter.

+4  A: 

Java 7 might or might not implement closures and hence support functionality like this, but currently it doesn't, so on the Java VM you have the options to do it in Groovy, Scala or Clojure (possible others, too), but in java you can only get close to that by using helpers like Guava's Collections2.filter().

JDK 7 sample code:

findItemsLargerThan(List<Integer> l, int what){
   return filter(boolean(Integer x) { x > what }, l);
}  
findItemsLargerThan(Arrays.asList(1,2,5,6,9), 5)

Groovy sample code:

Arrays.asList(1,2,5,6,9).findAll{ it > 5}

Guava Sample Code:

Collections2.filter(Arrays.asList(1, 2, 5, 6, 9),
    new Predicate<Integer>(){
        @Override
        public boolean apply(final Integer input){
            return input.intValue() > 5;
        }
    }
);

Scala sample code (thanks Bolo):

Array(1, 2, 5, 6, 9) filter (x => x > 5)
seanizer
Or Java 8: http://blogs.sun.com/mr/entry/rethinking_jdk7
Thilo
@Thilo argh, bad news. thx for the link
seanizer
thanks for the information!
CaptnLenz
In Scala it would be: `Array(1, 2, 5, 6, 9) filter (x => x > 5)`
Bolo
@Bolo thanks, added that code to my answer
seanizer
A: 

No, this kind of dynamic language construct is not supported in Java yet :-) So you have to live with your option 2

Jaydeep
+3  A: 

You can give a look at lambdaj. There is a select method you can use with a hamcrest condition.

Colin Hebert
Wow, that's very cool, too (+1)
seanizer
+1  A: 

Nothing is impossible (-:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ListCleaner {

    public static void main(String[] args) {

        final List<Integer> oldList = Arrays.asList(new Integer[] { 23, 4, 5,
                657 });
        System.out.println(oldList);

        List<Integer> newList = new ArrayList<Integer>() {
            {
                for (Integer element : oldList) {
                    if (element > 5) {
                        this.add(element);
                    }
                }
            }

        };
        System.out.println(newList);
    }
}

The only constraint is that the oldList has to be final.

Adriaan Koster
+1  A: 

It can be done in pure Java, but you need to write a support class Filter for the following code to run successfully:

    List<Integer> oldList = Arrays.asList(new Integer[] { 1, 2, 5, 6, 9 });
    List<Integer> newList = new Filter<Integer>(oldList) {
        {
            findAll(it > 5);
        }
    }.values();
    System.out.println(newList); // [6, 9] 

In case you wonder why this code compiles take a look at Hidden features of Java: Double brace initialization. This creates an anonymous instance of the class Filter that contains the it variable and provides the method findAll().

The Filter class itself has the one drawback that a new instance is created for each list element to evaluate the boolean condition at findAll():

public abstract class Filter<T> {

    protected List<T> values = new ArrayList<T>();

    protected T it;

    public Filter(List<T> values) {
        if (values != null) {
            this.values.addAll(values);
        }
        if (values.isEmpty()) {
            throw new RuntimeException("Not for empty collections!");
        }
        it = values.iterator().next();
        // instance initializer gets executed here, calls findAll
    }

    protected void findAll(boolean b) throws Throwable {
        // exit condition for future calls
        if (values.size() > 1) {
            // only repeat for each entry, if values has multiple entries
            Iterator<T> iterator = values.iterator();
            while (iterator.hasNext()) {
                // don't evalute again for the first entry
                if (!b) {
                    iterator.next();
                    iterator.remove();
                    b = true;
                } else {
                    // for each other entry create an argument with one element
                    List<T> next = new ArrayList<T>();
                    next.add(iterator.next());
                    // get constructor of anonymous class
                    Constructor<?> constructor = this.getClass().getDeclaredConstructors()[0];
                    // invoke constructor and thus execute instance initializer again
                    Filter<T> filtered = (Filter<T>) constructor.newInstance(new Object[] { null, next });
                    // if values is empty, the condition didn't match and the element can be removed
                    if (filtered.values.isEmpty()) {
                        iterator.remove();
                    }
                }
            }
        } else {
            // one element can be checked directly
            if (!b) {
                values.clear();
            }
        }
    }

    public List<T> values() {
        return values;
    }

}

But as instance creation is rather cheap these days and the Filter class is usable for all Objects, it may be worth including in your Utils package.

Greetz, GHad

GHad