2015-07-13 6 views
8

In questo momento mi può implementare solo il prodotto cartesiano di due collezioni, ecco il codice:Implementare cartesiana del prodotto delle collezioni da Java 8

public static <T1, T2, R extends Collection<Pair<T1, T2>>> 
R getCartesianProduct(
     Collection<T1> c1, Collection<T2> c2, 
     Collector<Pair<T1, T2>, ?, R> collector) { 
    return c1.stream() 
      .flatMap(e1 -> c2.stream().map(e2 -> new Pair<>(e1, e2))) 
      .collect(collector); 
} 

Questo codice funziona bene in IntelliJ, ma non in Eclipse (entrambi con livello di conformità del compilatore di 1,8):

The method collect(Collector<? super Object,A,R>) 
in the type Stream<Object> is not applicable for 
the arguments (Collector<Pair<T1,T2>,capture#5-of ?,R>) 

Ecco Pair.java:

public class Pair<T1, T2> implements Serializable { 
    protected T1 first; 
    protected T2 second; 
    private static final long serialVersionUID = 1360822168806852921L; 

    public Pair(T1 first, T2 second) { 
     this.first = first; 
     this.second = second; 
    } 

    public Pair(Pair<T1, T2> pair) { 
     this(pair.getFirst(), pair.getSecond()); 
    } 

    public T1 getFirst() { 
     return this.first; 
    } 

    public T2 getSecond() { 
     return this.second; 
    } 

    public void setFirst(T1 o) { 
     this.first = o; 
    } 

    public void setSecond(T2 o) { 
     this.second = o; 
    } 

    public String toString() { 
     return "(" + this.first + ", " + this.second + ")"; 
    } 

    @Override 
    public boolean equals(Object o) { 
     if(!(o instanceof Pair)) 
      return false; 
     Pair p = (Pair) o; 
     if(!this.first.equals(p.first)) 
      return false; 
     if(!this.second.equals(p.second)) 
      return false; 
     return true; 

    } 

    @Override 
    public int hashCode() { 
     int hash = 1; 
     hash = hash * 31 + this.first.hashCode(); 
     hash = hash * 31 + this.second.hashCode(); 
     return hash; 
    } 

} 

Come risolvere questo errore?

Esiste un modo elegante per implementare il prodotto cartesiano di varie collezioni? (supponiamo di avere la classe tuple)

+0

Che cosa significa 'coppia'? puoi dare l'importazione per quello? – Shahzeb

risposta

8

Eclipse ha problemi con l'inferenza di tipo. Se aggiungi un suggerimento tipo .<Pair<T1,T2>>flatMap, esso viene compilato correttamente.

Se posso suggerire un approccio diverso, considerare la vostra prodotto cartesiano non fare l'intero flusso e la raccolta, ma semplicemente essere un aiuto per flatMap:

static <T1, T2, R> Function<T1, Stream<R>> crossWith(
     Supplier<? extends Stream<T2>> otherSup, 
     BiFunction<? super T1, ? super T2, ? extends R> combiner 
) { 
    return t1 -> otherSup.get().map(t2 -> combiner.apply(t1, t2)); 
} 

Ora avete solo bisogno di creare un Pair se si desidera che il risultato contiene Pair s e si può fare un prodotto cartesiano di ordine superiore mediante l'applicazione di flatMap più volte:

List<String> letters = Arrays.asList("A", "B", "C"); 
List<Integer> numbers = Arrays.asList(1, 2, 3); 

List<Pair<String, Integer>> board = letters.stream() 
       .flatMap(crossWith(numbers::stream, Pair::new)) 
       .collect(toList()); 


List<String> ops = Arrays.asList("+", "-", "*", "/"); 

List<String> combinations = letters.stream() 
       .flatMap(crossWith(ops::stream, String::concat)) 
       .flatMap(crossWith(letters::stream, String::concat)) 
       .collect(toList()); // triple cartesian product 
+0

In questo modo, i tempi di chiamata di 'flatMap' sono determinati al momento della compilazione. – stanleyerror

+0

Intendo dire che potrei conoscere le raccolte e chiamare manualmente 'flatMap', piuttosto che determinare i tempi di chiamata' flatMap' in fase di esecuzione, e ottenere una raccolta generale di 'tuple's ciascuna memorizzata in una' tupla'. – stanleyerror

+1

Non sono sicuro di aver capito. Puoi descrivere più chiaramente il problema che stai cercando di risolvere? – Misha

2

Ecco una soluzione che generalizza al caso in cui il num b di applicazioni flatMap richieste (i. e. l'ordine del prodotto) non è noto al momento della compilazione.

BinaryOperator<Function<String,Stream<String>>> kleisli = (f,g) -> s -> f.apply(s).flatMap(g); 

List<String> cartesian(int n, Collection<String> coll) { 
    return coll.stream() 
      .flatMap(IntStream.range(1, n).boxed() 
        .map(_any -> crossWith(coll::stream, String::concat)) // create (n-1) appropriate crossWith instances 
        .reduce(s -> Stream.of(s), kleisli)     // compose them sequentially 
        )              // flatMap the stream with the entire function chain 
      .collect(toList()); 
} 

Troverete dettagli di come funziona in my own blog.

+0

Vedere [questa domanda] (http://stackoverflow.com/q/32131987/4856258) che è ancora più generalizzata. –