开发者

Java oneliner for list cleanup

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

[] = [item for item in oldList if item.getInt() > 5]开发者_如何学Go

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.


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)


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


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.


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


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

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜