开发者

Working on the properties of the objects in a list?

Given the following Code:

public class MyObject { 

String myProperty;

public MyObject(String propertyValue) {
    myProperty=propertyValue; 
}
}

and

public class Main {

public static void main(String[] args) {

    ArrayList<MyObject> objects = new ArrayList<MyObject>();
    objects.add(new MyObject("first"));
    objects.add(new MyObject("second"));
    objects.add(new MyObject("third"));

    // Now do Task A and Task B
}
}

Now I am looking for the best way to do the following:

Task A: Find all objects where myProperty equals "second". Is there something like

ArrayList<MyObject> filteredObjects = objects.findPropertyValue(myProperty, "second") ?

Task B: Extract the different myProperty values from the list, meaning I want to get an array which includes ("first","second","third") Is there something like

ArrayList propertyValues = objects.getPropertyValues(myProperty) ?

I know that Task A and B can be solved by looping through the ArrayLi开发者_C百科st, but I am wondering if there is a better way / already something built-in in Eclipse? Thanks for any hint :-)

Please note I do not want to use external libraries (I am currently developing on android and want to keep my application small).


If you need the first one (Task A), it may indicate the ArrayList is not the optimal data structure you should be using. This type of access should make you consider using a Map or a MultiMap (implemented in Apache Commons Collections).

But... If you really need this kind of stuff. Several libraries come in handy.

Recently popular is Guava. Another one is LambdaJ which seems more specialized:

// Task A
filter(having(on(MyObject.class).getPropertyValue(), equalTo("second")), objects);

// Task B
convert(objects, new PropertyExtractor("propertyValue"));
// or even
extract(objects, on(MyObject.class).getPropertyValue());

(I didn't have a chance to compile/run the code I typed in, so please don't be too strict about it)


... but lets then say i am looking for the best way in java without external libraries?

In that case, you best approach is to write loops.

It is possible to build a solution based on reflective access to the named fields, but the code is not simple and certainly not performant.

... so I guess I am getting killed by my team mates if I introduce an external library which I use only for one small thing.

Your workmates would be inclined to kill you for doing this using reflection as well. The best way to ensure your survival with your gonads still attached ( :-) ) is to write the loops.


Eclipse would not directly help you in such task - it can only help in creating the program - it doesn't create on itself.

If adding a new library to the project is not an option, you can write your own generic solution. However, I strongly suggest you'd look into adding some common library like Guava or Apache Commons as suggested before.

The two tasks that you are attempting to achieve can be more generally described as filtering and mapping (aka selecting, projecting). The filtering can be generalized by using a Predicate - a simple function which will return whether the object should be included in the new collection. The mapping can be achieved by a generic function which takes one source object (in your case, MyObject), and returns an object of another type (in your case, String).

Those set (collection) operations are supported more easily by languages, some of them compiled to the JVM, such as python (jython), groovy and scala. But I'm guessing it's not an option to use a different language for the project.

So I'll demonstrate how it can be achieved in Java, though the syntax is a bit clunkier, since we'll use anonymous classes for the predicate and the convertor.

There is a simple example of a DIY filtering at What is the best way to filter a Java Collection? , so I'll use it in the example.

The two interfaces we need:

public interface Convertor<FROM, TO> {
         TO convert(FROM from);
    }

public interface Predicate<T> {
    boolean apply(T type);
}

And the actual "library":

import java.util.ArrayList;
import java.util.Collection;


public class CollectionUtils {
    public static <FROM, TO> Collection<TO> select(Collection<FROM> from, Convertor<FROM, TO> convertor) {
        Collection<TO> result = new ArrayList<TO>();
        for (FROM fromItem: from) {
            result.add(convertor.convert(fromItem));
        }
        return result;
    }

    public static <T> Collection<T> filter(Collection<T> target, Predicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element: target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

}

Now we can easily achieve the two tasks:

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

public class Main {

    public static void main(String[] args) {

        ArrayList<MyObject> objects = new ArrayList<MyObject>();
        objects.add(new MyObject("first"));
        objects.add(new MyObject("second"));
        objects.add(new MyObject("third"));

        // Now do Task A and Task B

        // Task A: Filter
        Collection<MyObject> filtered = CollectionUtils.filter(objects, new Predicate<MyObject>() {

            @Override
            public boolean apply(MyObject myObject) {
                return myObject.myProperty.equals("second");
            }
        });
        for (MyObject myObject: filtered) {System.out.println(myObject.myProperty);}
        System.out.println();

        // TASK B: Map/Select
        Collection<String> transforemd = CollectionUtils.select(objects, new Convertor<MyObject, String>() {

            @Override
            public String convert(MyObject from) {
                return from.myProperty;
            }
        });

        for (String str: transforemd) {System.out.println(str);}
    }
}

It would be very simple now to change the predicate to filter other objects, or create different Strings, or even other types instead of script: just change the apply() and convert() functions! (Oh well, and some generics hints :) ).

Disclaimer: The code is not thoroughly tested, just rolled out for demonstration, and may have some other issues (such as allocating ArrayList for new collections). This is why it's usually better to use well tested and debugged 3rd party libraries!


Admittedly, the following is a bit of advertising my work, but it does exactly fit your problem.

First off, somebody has to do the work. Either you do it yourself, i.e. write the necessary loops yourself, or you have some library do the work for you.

I don't know about the other libs suggested here, but if you care to have a look at http://tbull.org/projects/util4j/, this is a small lib I wrote myself and the license permits it to take the things you need from the source code and include it in your own source.

In this approach, specifically crafted Greppers and Mappers are used, so there's no need for reflection. It is indeed very much like what Amitay Dobo already posted, but this lib provides for more versatile modes of operation, including reading the input elements from Iterators and lazy grepping/mapping.

Task A: Find all objects where myProperty equals "second". Is there something like

ArrayList<MyObject> filteredObjects = objects.findPropertyValue(myProperty, "second") ?

This would be done like

import org.tbull.util.Collections;
import org.tbull.util.Grepper;

private static class PropertyGrepper implements Grepper<MyObject> {
    public final String property;
    public PropertyGrepper(String property) {
        this.property = property;
    }
    public @Override boolean grep(MyObject element) {
        return element.myProperty.equals(property);
    }
}

List<MyObject> filteredObjects = Collections.grep(new PropertyGrepper("second"), objects);

Task B: Extract the different myProperty values from the list, meaning I want to get an array which includes ("first","second","third") Is there something like

ArrayList propertyValues = objects.getPropertyValues(myProperty) ?
import org.tbull.util.Collections;
import org.tbull.util.Mapper;

private static class PropertyExtractor implements Mapper<MyObject, String> {
    public @Override String map(MyObject element) {
        return element.myProperty;
    }
}

List<String> propertyValues = Collections.map(new PropertyExtractor(), objects);

Disclaimer: This code is untested. myProperty has to be accessible for this to work, either being public or being provided by a getter function.

Of course, you can make use of anonymous inner classes for brevity or implement the Grepper/Mapper publicly for reusability.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜