views:

462

answers:

6

I'm writing an adapter framework where I need to convert a list of objects from one class to another. I can iterate through the source list to do this as in

http://stackoverflow.com/questions/18524/java-best-way-of-converting-listinteger-to-liststring

However, I'm wondering if there is a way to do this on the fly when the target list is being iterated, so I don't have to iterate through the list twice.

A: 

That question does not iterate through the list twice. It just iterates once and by far is the only known method.

Also you could use some transformer classes in commons-collections of google-collections but they all do the same thing under the hood :) the following being one way

CollectionUtils.collect(collectionOfIntegers, new org.apache.commons.collections.functors.StringValueTransformer());
Calm Storm
Isn't the list iterated (at least) twice; once on the original list when the conversion is done and then again when the target list is iterated over?
appy
A: 

Well, you could create your own iterator wrapper class to do this. But I doubt that you would save much by doing this.

Here's a simple example that wraps any iterator to a String iterator, using Object.toString() to do the mapping.

public MyIterator implements Iterator<String> {

    private Iterator<? extends Object> it;

    public MyIterator(Iterator<? extends Object> it) {
        this.it = it;
    }

    public boolean hasNext() {
        return it.hasNext();
    }

    public String next() {
        return it.next().toString();
    }

    public void remove() {
        it.remove();
    }
}
Stephen C
+1  A: 

You can write a mapping iterator that decorates an existing iterator and applies a function on it. In this case, the function transforms the objects from one type to another "on-the-fly".

Something like this:

import java.util.*;

abstract class Transformer<T, U> implements Iterable<U>, Iterator<U> {
    public abstract U apply(T object);  

    final Iterator<T> source;       
    Transformer(Iterable<T> source)    { this.source = source.iterator(); }

    @Override public boolean hasNext() { return source.hasNext(); }
    @Override public U next()          { return apply(source.next()); }
    @Override public void remove()     { source.remove(); } 

    public Iterator<U> iterator()      { return this; }
}

public class TransformingIterator { 
    public static void main(String args[]) {
        List<String> list = Arrays.asList("1", "2", "3");
        Iterable<Integer> it = new Transformer<String, Integer>(list) {
            @Override public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        for (int i : it) {
            System.out.println(i);
        }
    }
}
polygenelubricants
This looks close to what I'm looking for. As per the similar response, does doing this on the fly give me much advantage?
appy
Honestly, I think the Google Collections solution (Ben Lings') might be better. Their design is a lot more mature, and their implementation is more robust. Frankly I just whipped that code up in 15 minutes without putting too much thought into it.
polygenelubricants
I mean, the class implements `Iterator<U>` and `Iterable<U>`. I'm not sure if that's kosher.
polygenelubricants
A: 

I think you would either have to create a custom List (implementing the List interface) or a custom Iterator. For example:

ArrayList<String> targetList = new ArrayList<String>();
ConvertingIterator<String> iterator = new ConvertingIterator<String>(targetList);
// and here you would have to use a custom List implementation as a source List
// using the Iterator created above

But I doubt that this approach would save you much.

Tedil
+2  A: 

My answer to that question applies to your case:

import com.google.common.collect.Lists;
import com.google.common.base.Functions

List<Integer> integers = Arrays.asList(1, 2, 3, 4);

List<String> strings = Lists.transform(integers, Functions.toStringFunction());

The transformed list is a view on the original collection, so the transformation happens when the destination List is accessed.

Ben Lings
A: 

Lambdaj allows to do that in a very simple and readable way. For example, supposing you have a list of Integer and you want to convert them in the corresponding String representation you could write something like that;

List<Integer> ints = asList(1, 2, 3, 4);
Iterator<String> stringIterator = convertIterator(ints, new Converter<Integer, String> {
    public String convert(Integer i) { return Integer.toString(i); }
});

Lambdaj applies the conversion function only while you're iterating on the result. There is also a more concise way to use the same feature. The next example works supposing that you have a list of persons with a name property and you want to convert that list in an iterator of person's names.

Iterator<String> namesIterator = convertIterator(persons, on(Person.class).getName());

Pretty easy. Isn't it?

Mario Fusco