views:

181

answers:

6

This may be a little subjective, but I have often found that it can be very interesting to see how other developers approach certain daily details.

I have code which works like this:

class A {
    public List<SomeType> getOneSet() { ... }
    public List<SomeType> getAnotherSet() { ... }
}

class B {
    public static OtherType convert(SomeType input) { ... }
}

// ...

A a = new A();
List<OtherType> rgResults = new ArrayList<OtherType>();

And now would follow the equivalent of two identical for loops, like so:

for (SomeType input : a.getOneSet()) {
    rgResults.add(B.convert(input);
}

for (SomeType input : a.getAnotherSet()) {
    rgResults.add(B.convert(input);
}

This works, but it's of course code duplication. If the code inside the loop gets a little more complicated or there's more than two sets it's not acceptable.

I therefore put the loop in a function that takes source and destination list as a parameter, but was curious to see if there are other ways. Especially ones that might be more appropriate when you're never calling the function from more than one place.

For example I would have liked the following, which didn't work because I can't have arrays of generics:

for (List<SomeType> rgSrc : new List<SomeType>[] { a.getOneSet(), a.getAnotherSet() } ) {
    for (SomeType src : rgSrc) {
        rgResults.add(B.convert(src));
    }
}
+6  A: 

Try IteratorUtils. This has a method to chain iterators.

Aaron Digulla
Cool, but in my case, I didn't want to add yet another library just for that little perk. Went with the interfaces.
Hanno Fietz
+2  A: 

Try something like this:

interface Getter<E, I> 
{
  public E get(I item);
}

public static <T extends Collection<E>, I, E> T convert(T target, Collection<I> source, Getter<E, I> getter)
{
  for (I item : source)
  {
    target.add(getter.get(item));
  }

  return target;
}

List<String> strings = CollectionUtil.convert(
  new ArrayList<String>(someItems.size)
  someItems,
  new Getter<String, MyClass>() 
  {
    public String get(MyClass item)
    {
      return item.toString();
    }
  }
);

You simply implement Getter as necessary depending on the converstion you want to do.

Nick Holt
+1: You could also create ArrayList with a capacity source.size().
Adamski
@Adamski: indeed you could. I'd typically be a little more sophisticated in how the returned List (or what should really be a Collection) is created so that the calling code can control this. In fact, let me modify the example now.
Nick Holt
+2  A: 

ListUtils function union(java.util.List list1, java.util.List list2) from Apache commons collection API shall do the job ==> union as well =]

Narayan
+1  A: 

For a solution using no loops at all, you could use lambdaj convert functionality:

see http://code.google.com/p/lambdaj/wiki/LambdajFeatures

Pablojim
+2  A: 

Following the principle of 'use someone else's code', I think the cleanest implementation you'll find will be in Google Collections' Iterables class.

You could do:

for (SomeType input : Iterables.concat(a.getOneSet(), a.getAnotherSet()) {
  rgResults.add(B.convert(input);
}

Or, if you rewrite B as a Function and use Lists:

rgResults = Lists.transform(
    Lists.newArrayList(Iterables.concat(a.getOneSet(), a.getAnotherSet()), 
    new B());

and you're done!

Cowan
+1  A: 

Remove the generic argument from your array, and your loop should work (although you'll get a warning):

for (List<SomeType> rgSrc : new List[] { a.getOneSet(), a.getAnotherSet() } ) {
    for (SomeType src : rgSrc) {
        rgResults.add(B.convert(src));
    }
}
Adam Crume