Вот Iterable, который позволяет использовать упрощенный цикл for:
import java.util.*;
// let's begin with the demo. Instead of Person and Gift,
// I use the well known char and int.
class CartesianIteratorTest {
public static void main (String[] args) {
List <Object> lc = Arrays.asList (new Object [] {'A', 'B', 'C', 'D'});
List <Object> lC = Arrays.asList (new Object [] {'a', 'b', 'c'});
List <Object> li = Arrays.asList (new Object [] {1, 2, 3, 4});
// sometimes, a generic solution like List <List <String>>
// might be possible to use - typically, a mixture of types is
// the common nominator
List <List <Object>> llo = new ArrayList <List <Object>> ();
llo.add (lc);
llo.add (lC);
llo.add (li);
// Preparing the List of Lists is some work, but then ...
CartesianIterable <Object> ci = new CartesianIterable <Object> (llo);
for (List <Object> lo: ci)
show (lo);
}
public static void show (List <Object> lo) {
System.out.print ("(");
for (Object o: lo)
System.out.print (o + ", ");
System.out.println (")");
}
}
Как это сделать? Нам нужен Iterable, чтобы использовать упрощенный цикл for, и Iterator должен быть возвращен из Iterable.
Мы возвращаем список объектов - это может быть Set вместо List, но Set не имеет индексированного доступа, поэтому было бы немного сложнее реализовать его с помощью Set вместо List. Вместо универсального решения Object мог бы подойти для многих целей, но универсальные решения допускают дополнительные ограничения.
class CartesianIterator <T> implements Iterator <List <T>> {
private final List <List <T>> lilio;
private int current = 0;
private final long last;
public CartesianIterator (final List <List <T>> llo) {
lilio = llo;
long product = 1L;
for (List <T> lio: lilio)
product *= lio.size ();
last = product;
}
public boolean hasNext () {
return current != last;
}
public List <T> next () {
++current;
return get (current - 1, lilio);
}
public void remove () {
++current;
}
private List<T> get (final int n, final List <List <T>> lili) {
switch (lili.size ())
{
case 0: return new ArrayList <T> (); // no break past return;
default: {
List <T> inner = lili.get (0);
List <T> lo = new ArrayList <T> ();
lo.add (inner.get (n % inner.size ()));
lo.addAll (get (n / inner.size (), lili.subList (1, lili.size ())));
return lo;
}
}
}
}
Математическая работа выполняется в методе get. Подумайте о 2 наборах из 10 элементов. Всего имеется 100 комбинаций, перечисленных от 00, 01, 02, ... 10, ... до 99. Для 5 X 10 элементов 50, для 2 X 3 элементов 6 комбинаций. Модуль размера подсписка помогает выбрать один элемент для каждой итерации.
Итерируемо, здесь наименее интересная вещь:
class CartesianIterable <T> implements Iterable <List <T>> {
private List <List <T>> lilio;
public CartesianIterable (List <List <T>> llo) {
lilio = llo;
}
public Iterator <List <T>> iterator () {
return new CartesianIterator <T> (lilio);
}
}
Чтобы реализовать Iterable, который допускает цикл для каждого вида, мы должны реализовать iterator (), а для Iterator мы должны реализовать hasNext (), next () и remove ().
Результат:
(A, a, 1, )
(B, a, 1, )
(C, a, 1, )
(D, a, 1, )
(A, b, 1, )
(B, b, 1, )
(C, b, 1, )
(D, b, 1, )
...
(A, a, 2, )
...
(C, c, 4, )
(D, c, 4, )