0

In our project, we use proxies-based database API (Tinkerpop Frames), so we have a lot of loops like:

    List<Link> links = new LinkedList<>();
    for (LinkModel model : obj.getLinks())
    {
        Link l = new Link(model.getLink(), model.getDescription());
        links.add(l);
    }

I would like to get rid of these for two reasons:

  1. To remove boilerplate code
  2. For larger lists, memory issues may arise.

Is there a nice way to get an Iterable that takes from the other one and converts using given method? What I would like to do is:

Iterable<Link> links_ = new IterableConverter<LinkModel, Link>(obj.getLinks()){
    public Link from(LinkModel m){ return new Link(m.getLink(), m.getDescription()); }
};

I guess there's something like that in Java 8. I need this for Java 7.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • 2
    [Guava's `Iterables.transform()`](http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Iterables.html#transform(java.lang.Iterable,%20com.google.common.base.Function))? – Andy Turner Apr 20 '16 at 23:13

1 Answers1

0

I've spent a while battling the generics, and here's the result, which seems to work fine:

import java.util.Iterator;

/**
 * An Iterable that takes from the other Iterable and converts the items using given method.
 * The primary reason is to prevent memory issues which may arise with larger lists.
 *
 *  @author Ondrej Zizka, ozizka at redhat.com
 */
public abstract class IterableConverter<TFrom, TTo> implements Iterable<TTo>, Converter<TFrom, TTo>
{
    final Iterable<TFrom> sourceIterable;

    public IterableConverter(Iterable<TFrom> sourceIterable)
    {
        this.sourceIterable = sourceIterable;
    }

    public abstract TTo from(TFrom m);


    @Override
    public Iterator<TTo> iterator()
    {
        return new IteratorBacked<TFrom, TTo>(sourceIterable.iterator(), this);
    }

    class IteratorBacked<TFromX extends TFrom, TToX extends TTo> implements Iterator<TToX> {

        private final Iterator<TFromX> backIterator;
        private final Converter<TFromX, TToX> converter;

        public IteratorBacked(Iterator<TFromX> backIterator, Converter<TFromX, TToX> converter)
        {
            this.backIterator = backIterator;
            this.converter = converter;
        }

        @Override
        public boolean hasNext()
        {
            return backIterator.hasNext();
        }


        @Override
        public TToX next()
        {
            return converter.from(backIterator.next());
        }

    }
}

interface Converter<TFromC, TToC> {
    TToC from(TFromC m);
}
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
  • 1
    That looks reasonably similar to how Guava's `Iterables.transform` (logically) works, except that uses composition rather than inheritance to provide the conversion method (which is a much better approach, since you can reuse `Converter` instances in multiple transformed iterables). – Andy Turner Apr 20 '16 at 23:28
  • And note that because you've made `IteratorBacked` a nested class, you don't need to duplicate things like the `converter` reference, and the additional type variables are redundant. – Andy Turner Apr 20 '16 at 23:35
  • Actually, originally I had it not duplicated, but the generics engine was not happy with it. It internally turns the same names into TTo#1 and TTo#2, and things went south. Maybe I could make it better in that way. But the API would be the same. – Ondra Žižka Apr 21 '16 at 01:12
  • Regarding composition vs. inheritance - for my use case, inheritance is better, because it allows shorter code, and the conversions are typically unique. – Ondra Žižka Apr 21 '16 at 01:18