Java join collections using functor

2020-06-25 04:34发布

问题:

2 collections are given with the same number of elements, say List<String>. What are elegant ways in JAVA to apply a functor on each 2 elements of collections with corresponding indexes?

Say, one example could be:
List<String> = { "APPLE", "PEAR" };
List<String> = { "BANANA", "ORANGE" };

A predicate that joins string together will result in the following List<String>:
List<String> = { "APPLEBANANA", "PEARORANGE" };

回答1:

Akin to the functors found in Apache Commons Collections, I have created binary equivalents in the past.

For your situation, a binary transformer type object, which takes to two input objects and returns a single object, could be used. Here is some sample code that's conveys my approach:

// tranformer
interface BinaryTransformer<X, Y, Z> {
  Z transform(X a, Y b);
}

// implementation for your problem
class ConcatTransformer implements BinaryTransformer<String, String, String> {
  public String transform(String a, String b) {
    return a + b;
  }
}

// general use transformer
class BinaryListUtils {
  public static <X, Y, Z> List<Z> collect(List<X> aList, List<Y> bList, BinaryTransformer<X, Y, Z> t) {
    List<Z> ret = new ArrayList<Z>(aList.size());
    Iterator<X> aIter = aList.iterator();
    Iterator<Y> bIter = bList.iterator();
    while(aIter.hasNext()) {
      ret.add(t.transform(aIter.next(), bIter.next()));
    }
  }
}

HTH



回答2:

A quick driver of this showed it to work. Not responsible for all test cases. :-)

List<String> combineListsHorizontally(List<String> a, List<String> b) {
    assert a.size() == b.size(); // just avoids some checks

    List<String> result = new ArrayList<String>(a.size());

    Iterator<String> itera = a.iterator();
    Iterator<String> iterb = b.iterator();

    for(int i = 0; i < a.size(); i++) {
        String combined = itera.next() + iterb.next();
        result.add(combined);
    }
    return result;

}

If you need something generic, you would need to know they ahve a way that they can be joined

 List<E> combineListsHorizontally(List<E> a, List<E> b) {
        assert a.size() == b.size(); // just avoids some checks

        List<E> result = new ArrayList<E>(a.size());

        Iterator<E> itera = a.iterator();
        Iterator<E> iterb = b.iterator();

        for(int i = 0; i < a.size(); i++) {
            E combined = new MagicCombiner<E>(a,b).get(); // define this line yourself
            result.add(combined);
        }
        return result;

    }

///////////////// EDIT - here's a working example based off @Brents (superior) example. Props to him for illustrating this pattern better than I did.

import java.util.*;

/**
 * Compile: "javac BinaryListUtils"
 * Usage: "java BinaryListUtils"

 C:\Documents and Settings\user\My Documents>javac BinaryListUtils.java

 C:\Documents and Settings\user\My Documents>java BinaryListUtils
 APPLEBANANA
 PEARORANGE

 C:\Documents and Settings\user\My Documents>
 */

// general use transformer
class BinaryListUtils {

    // tranformer
    static interface BinaryTransformer<X, Y, Z> {
        Z transform(X a, Y b);
    }

    // implementation for your problem
    static class ConcatTransformer implements BinaryTransformer<String, String, String> {
        public String transform(String a, String b) {
            return a + b;
        }
    }

    public static <X, Y, Z> List<Z> collect(List<X> aList, List<Y> bList, BinaryTransformer<X, Y, Z> t) {
        List<Z> ret = new ArrayList<Z>(aList.size());
        Iterator<X> aIter = aList.iterator();
        Iterator<Y> bIter = bList.iterator();
        while(aIter.hasNext()) {
            ret.add(t.transform(aIter.next(), bIter.next()));
        }
        return ret;
    }

    public static void main(String[] args) {

        List<String> aList = new ArrayList<String>();
        List<String> bList = new ArrayList<String>();

        aList.add("APPLE");
        aList.add("PEAR");

        bList.add("BANANA");
        bList.add("ORANGE");

        ConcatTransformer ct = new ConcatTransformer();

        List<String> cList = BinaryListUtils.collect(aList,bList,ct);

        for(String s : cList) System.out.println(s);


    }
}


回答3:

What you're asking for isn't a predicate. It's doing a transformation on the lists zipped together. The generic way to do this is to write an iterable zipper that will zip the two lists into an iterable of a Pair, and then apply the transformation to the pairs.

I initially thought you were asking for the intersection of two collections, which is supplied in Guava collections as Sets.intersection(Set, Set).



回答4:

I think your best bet here will be to do it iteratively. I can't think of any core Java API that can do it.

public List<String> predicate(List<String> list1, List<String> list2) {
    List<String> list = new ArrayList<String>();

    for(int i = 0; i < list1.size(); i++) {
        list.add(new StringBuilder(list1.get(i)).append(list2.get(i)).toString());
    }

    return list;
}

Haven't compiled / run it. Good luck.