Aquí se presenta una Iterable , que le permite usar un bucle forzado simplificado:
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 (")");
}
}
¿Cómo se hace? Necesitamos un Iterable, para usar el for-loop simplificado, y un Iterator debe ser devuelto desde Iterable. Devolvemos una Lista de objetos: puede ser un conjunto en lugar de una lista, pero Set no tiene acceso indexado, por lo que sería un poco más complicado implementarlo con Set en lugar de List. En lugar de una solución genérica, Object habría estado bien para muchos propósitos, pero los genéricos permiten más restricciones.
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;
}
}
}
}
El trabajo matemático se realiza en el 'método get'. Piensa en 2 conjuntos de 10 elementos. Tienes un total de 100 combinaciones, enumeradas entre 00, 01, 02, ... 10, ... a 99. Para 5 X 10 elementos 50, para 2 X 3 elementos 6 combinaciones. El módulo del tamaño de la sublista ayuda a elegir un elemento para cada iteración.
Iterable i lo menos interesante aquí:
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);
}
}
Para implementar Iterable, que permite que el para-cada tipo de bucle, tenemos que implementar iterador(), y para el iterador tenemos que aplicar hasNext (), next() y remove().
Resultado:
(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,)
¿Podría explicar qué es exactamente lo que está tratando de lograr? – Rik
Esta pregunta es muy interesante desde un punto de vista teórico, académico. Me sorprendió lo difícil que es encontrar una solución limpia para una pregunta fácil como esta: si hubiera encontrado una, habría respondido. Pero teniendo esto dicho ... –
... su pregunta parece apuntar a una aplicación específica, y me parece que va a perder todo tipo de letra si solo inserta todo en conjuntos y esos conjuntos en un producto cartesiano. Tal vez su enfoque es seriamente defectuoso al pensar en matemáticas y en pocos en términos de OOP? –