Java SE 8 ha coppie o tuple?

Sto giocando con le pigre operazioni funzionali in Java SE 8, e voglio map un indice i ad una coppia / tupla (i, value[i]) , quindi filter base al secondo elemento value[i] e alla fine emettere solo gli indici.

Devo ancora soffrire questo: qual è l’equivalente della coppia C ++ in Java? nell’audace nuova era di lambda e torrenti?

Aggiornamento: ho presentato un esempio piuttosto semplificato, che ha una soluzione chiara offerta da @dkatzel in una delle risposte di seguito. Tuttavia, non generalizza. Pertanto, vorrei aggiungere un esempio più generale:

 package com.example.test; import java.util.ArrayList; import java.util.stream.IntStream; public class Main { public static void main(String[] args) { boolean [][] directed_acyclic_graph = new boolean[][]{ {false, true, false, true, false, true}, {false, false, false, true, false, true}, {false, false, false, true, false, true}, {false, false, false, false, false, true}, {false, false, false, false, false, true}, {false, false, false, false, false, false} }; System.out.println( IntStream.range(0, directed_acyclic_graph.length) .parallel() .mapToLong(i -> IntStream.range(0, directed_acyclic_graph[i].length) .filter(j -> directed_acyclic_graph[j][i]) .count() ) .filter(n -> n == 0) .collect(() -> new ArrayList(), (c, e) -> c.add(e), (c1, c2) -> c1.addAll(c2)) ); } } 

Ciò fornisce un output errato di [0, 0, 0] che corrisponde ai conteggi per le tre colonne che sono tutti false . Quello di cui ho bisogno sono gli indici di queste tre colonne. L’output corretto dovrebbe essere [0, 2, 4] . Come posso ottenere questo risultato?

AGGIORNAMENTO: Questa risposta è in risposta alla domanda originale, Java SE 8 ha coppie o tuple? (E implicitamente, se no, perché no?) L’OP ha aggiornato la domanda con un esempio più completo, ma sembra che possa essere risolto senza utilizzare alcun tipo di struttura Pair. [Nota da OP: ecco l’altra risposta corretta .]


La risposta breve è no. È necessario eseguire il rollover o inserire una delle numerose librerie che la implementano.

Avere una class Pair in Java SE è stata proposta e respinta almeno una volta. Vedi questo thread di discussione su una delle mailing list di OpenJDK. I compromessi non sono ovvi. Da un lato, ci sono molte implementazioni di Pair in altre librerie e nel codice dell’applicazione. Ciò dimostra una necessità, e l’aggiunta di una class simile a Java SE aumenterà il riutilizzo e la condivisione. D’altra parte, avere una class Pair aggiunge la tentazione di creare strutture dati complicate da coppie e raccolte senza creare i tipi e le astrazioni necessari. (Questa è una parafrasi del messaggio di Kevin Bourillion tratto da quel thread.)

Consiglio a tutti di leggere tutta la discussione via email. È straordinariamente intuitivo e non ha alcuna influenza. È abbastanza convincente. Quando è iniziato ho pensato, “Sì, dovrebbe esserci una class Pair in Java SE” ma quando il thread ha raggiunto la fine ho cambiato idea.

Si noti tuttavia che JavaFX ha la class javafx.util.Pair . Le API di JavaFX si sono evolute separatamente dalle API Java SE.

Come si può vedere dalla domanda collegata Qual è l’equivalente della coppia C ++ in Java? c’è abbastanza spazio di design che circonda quella che apparentemente è una semplice API. Gli oggetti dovrebbero essere immutabili? Dovrebbero essere serializzabili? Dovrebbero essere comparabili? La class dovrebbe essere definitiva o no? I due elementi dovrebbero essere ordinati? Dovrebbe essere un’interfaccia o una class? Perché fermarsi alle coppie? Perché non tripli, quadricipiti o N-tuple?

E naturalmente c’è l’inevitabile menzione del numero di biciclette per gli elementi:

  • (a, b)
  • (primo secondo)
  • (sinistra destra)
  • (auto, cdr)
  • (foo, bar)
  • eccetera.

Un grosso problema che è stato appena menzionato è il rapporto tra coppie e primitivi. Se si ha un dato (int x, int y) che rappresenta un punto nello spazio 2D, rappresentando questo come Pair consuma tre oggetti invece di due parole a 32 bit. Inoltre, questi oggetti devono risiedere nell’heap e incorrere in overhead GC.

Sembrerebbe chiaro che, come gli stream, sarebbe essenziale che esistessero specializzazioni primitive per le coppie. Vogliamo vedere:

 Pair ObjIntPair ObjLongPair ObjDoublePair IntObjPair IntIntPair IntLongPair IntDoublePair LongObjPair LongIntPair LongLongPair LongDoublePair DoubleObjPair DoubleIntPair DoubleLongPair DoubleDoublePair 

Anche un IntIntPair richiederebbe ancora un object nell’heap.

Queste sono, naturalmente, una reminiscenza della proliferazione delle interfacce funzionali nel pacchetto java.util.function in Java SE 8. Se non vuoi un’API gonfia, quali non vuoi lasciare? Si potrebbe anche sostenere che questo non è sufficiente e che dovrebbero essere aggiunte anche le specializzazioni per, ad esempio, Boolean .

La mia sensazione è che se Java avesse aggiunto una class Pair molto tempo fa, sarebbe stato semplice, o anche semplicistico, e non avrebbe soddisfatto molti dei casi d’uso che stiamo immaginando ora. Considera che se la coppia fosse stata aggiunta nell’arco temporale di JDK 1.0, probabilmente sarebbe stata mutabile! (Guarda java.util.Date.) La gente sarebbe stata felice con quello? La mia ipotesi è che se ci fosse una class Pair in Java, sarebbe kinda-sort-not-really-useful e ognuno continuerà a girare il proprio per soddisfare i propri bisogni, ci sarebbero varie implementazioni Pair e Tuple in librerie esterne, e le persone continuerebbero a discutere / discutere su come riparare la class Pair di Java. In altre parole, tipo nello stesso luogo in cui ci troviamo oggi.

Nel frattempo, si sta lavorando per risolvere il problema fondamentale, che è il supporto migliore nella JVM (e infine nel linguaggio Java) per i tipi di valore . Vedi questo documento sullo stato dei valori . Questo è un lavoro preliminare, speculativo, e copre solo le questioni dal punto di vista di JVM, ma ha già una buona dose di pensiero dietro di esso. Ovviamente non ci sono garanzie che ciò avvenga su Java 9, o che si possa arrivare ovunque, ma mostra l’attuale direzione di pensiero su questo argomento.

Puoi dare un’occhiata a queste classi built-in:

  • AbstractMap.SimpleEntry
  • AbstractMap.SimpleImmutableEntry

Sembra che l’intero esempio possa essere risolto senza l’uso di alcun tipo di struttura Pair. La chiave è di filtrare gli indici delle colonne, con il predicato che controlla l’intera colonna, invece di mappare gli indici delle colonne sul numero di false voci in quella colonna.

Il codice che fa questo è qui:

  System.out.println( IntStream.range(0, acyclic_graph.length) .filter(i -> IntStream.range(0, acyclic_graph.length) .noneMatch(j -> acyclic_graph[j][i])) .boxed() .collect(toList())); 

Ciò si traduce in un output di [0, 2, 4] che credo sia il risultato corretto richiesto dall’OP.

Notare anche l’operazione boxed() che inserisce i valori int in oggetti Integer . Ciò consente di utilizzare il raccoglitore toList() preesistente, invece di dover scrivere funzioni di collector che eseguono il boxing stesso.

Purtroppo, Java 8 non ha introdotto coppie o tuple. È sempre ansible utilizzare org.apache.commons.lang3.tuple (che personalmente utilizzo in combinazione con Java 8) o creare i propri wrapper. Oppure usa Maps. O cose del genere, come è spiegato nella risposta accettata a quella domanda a cui ti sei collegato.

Dato che ti interessano solo gli indici, non devi assolutamente mappare le tuple. Perché non scrivere semplicemente un filtro che utilizza gli elementi di ricerca nell’array?

  int[] value = ... IntStream.range(0, value.length) .filter(i -> value[i] > 30) //or whatever filter you want .forEach(i -> System.out.println(i)); 

Vavr (precedentemente chiamato Javaslang) ( http://www.vavr.io ) fornisce anche tuple (fino alla dimensione di 8). Ecco la javadoc: https://static.javadoc.io/io.vavr/vavr/0.9.0/io/vavr/Tuple.html .

Questo è un semplice esempio:

 Tuple2 entry = Tuple.of(1, "A"); Integer key = entry._1; String value = entry._2; 

Perché JDK stesso non è venuto con un semplice tipo di tuple fino ad ora è un mistero per me. Scrivere classi wrapper sembra essere un business di tutti i giorni.

Sì.

Map.Entry può essere utilizzato come una Pair .

Sfortunatamente non è d’aiuto con i flussi di Java 8 poiché il problema è che anche se lambdas può assumere più argomenti, il linguaggio Java consente solo di restituire un singolo valore (object o tipo primitivo). Ciò implica che ogni volta che si ha un stream si finisce con il passare un singolo object dall’operazione precedente. Questa è una mancanza nel linguaggio Java, perché se sono stati supportati più valori di ritorno E gli stream li hanno supportati, potremmo avere attività non banali molto più belle fatte dagli stream.

Fino ad allora, c’è solo un piccolo uso.

MODIFICA 2018-02-12: Mentre lavoravo a un progetto, ho scritto una class helper che aiuta a gestire il caso speciale di avere un identificatore in precedenza nello stream di cui hai bisogno in un secondo momento, ma la parte del stream intermedio non lo sa. Fino a quando non riesco a liberarlo da solo, è disponibile su IdValue.java con un test unitario su IdValueTest.java

Le raccolte di Eclipse hanno una Pair e tutte le combinazioni di coppie primitive / object (per tutte e otto le primitive).

La fabbrica Tuples può creare istanze di Pair e la fabbrica di PrimitiveTuples può essere utilizzata per creare tutte le combinazioni di coppie di primitivi / oggetti.

Abbiamo aggiunto questi prima di rilasciare Java 8. Sono stati utili per implementare Iteratori di valore / chiave per le nostre mappe primitive, che supportiamo anche in tutte le combinazioni di primitivo / object.

Se si desidera aggiungere l’overhead della libreria extra, è ansible utilizzare la soluzione accettata di Stuart e raccogliere i risultati in un IntList primitivo per evitare il pugilato. Abbiamo aggiunto nuovi metodi in Eclipse Collections 9.0 per consentire la creazione di raccolte Int/Long/Double da Int/Long/Double Streams.

 IntList list = IntLists.mutable.withAll(intStream); 

Nota: sono un committer per le raccolte di Eclipse.