È una cosa sensata restituire gli stream ovunque verrebbero normalmente restituite le raccolte?

19

Durante lo sviluppo della mia API che non è legata a nessun codice legacy, mi trovo spesso a scrivere metodi che sono stati interrotti esclusivamente dalla pipeline di Streams raccogliendo i risultati. Come questo:

ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}

Ora, la maggior parte dei client di questa classe di solito hanno bisogno della Collection (in questo caso, un ImmutableSet) per cercare elementi e scorrere su di essa, ma alcuni client possono trarre vantaggio dall'avere un Stream così potrebbero reindirizzare alcune operazioni su quel flusso senza la necessità di ottenere un nuovo flusso dalla raccolta. Quindi restituire un flusso dà ai cliens un superset di opzioni che avrebbero se avessero appena la collezione (dopotutto, possono sempre collect() del flusso stesso:

Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}

Questo approccio mi sta tentando di provare perché non vedo alcun potenziale difetto che potrebbe avere. Tuttavia, non ho mai visto questo approccio in nessuna libreria (probabilmente perché non c'erano molte librerie rilasciate dopo l'apparizione di Java 8), quindi ho un po 'paura di adottarlo. Le classi di libreria esistenti in genere restituiscono le raccolte quando ricavano qualcosa dallo stato privato.

C'è qualcosa di brutto che potrebbe accadere se decido di restituire un flusso ovunque il mio sé pre-Java-8 restituisca una raccolta? O probabilmente sto facendo qualcosa di antipattern qui con tutto ciò che deriva dallo stato privato?

    
posta jojman 12.03.2015 - 19:46
fonte

3 risposte

14

Se myPrivateThingies è modificabile, hai creato una dipendenza nascosta tra lo stato privato e i risultati del flusso. Se è possibile che il client causi indirettamente myPrivateThingies a cambiare stato, allora otterrà un risultato diverso quando chiama collect rispetto a quello che originariamente intendevi distribuire.

Se myPrivateThingies è immutabile, il risultato sarà trasparente di riferimento, ma c'è un altro problema da tenere a mente: spazzatura semantica , ovvero aggrapparsi a grandi quantità di memoria che non sono più necessarie. Supponiamo che myPrivateThingies sia molto grande e il risultato della raccolta del flusso sia piccolo. Il client può conservare il flusso per molto tempo dopo aver gettato via tutti i riferimenti all'oggetto che lo ha prodotto, ma che stream sta ancora trattenendo myPrivateThingies dal diventare garbage collection. Raccogliere i risultati con entusiasmo consentirebbe di liberare myPrivateThingies .

Questo in realtà è accaduto prima di Java 7 quando si chiama substring . Oracle ha deciso che i potenziali risparmi di efficienza derivanti dal non copiare ogni volta la sottostringa non valgono la pena di sorprendere occasionalmente l'utente medio con un consumo eccessivo di memoria. Questo non vuol dire che non ci sono casi di utilizzo reali per il vecchio comportamento (ad esempio parser), ma spesso raccogliere i risultati con entusiasmo è abbastanza veloce e, quando ciò accade, non ci sono professionisti e potenziali truffatori.

D'altro canto, la restituzione di un flusso dà al cliente la possibilità di scegliere la struttura dati che desidera utilizzare per contenere i risultati, a differenza della scelta di uno per lui. Potrebbe valere la pena di offrire entrambe le opzioni.

    
risposta data 12.03.2015 - 19:56
fonte
4

La cosa più importante da considerare: Stream s può essere ripetuto una sola volta, mentre tu hai più flessibilità su un Collection : puoi continuare a creare più Stream s o anche Iterator s per fare ulteriori , elaborazione ripetitiva sui risultati.

Quindi, se non sei sicuro che i chiamanti del metodo utilizzeranno i risultati una sola volta e solo una volta, è meglio restituire un Collection .

Il tuo codice di esempio ha un errore evidente: perché un SocialStatus ha il concetto di persona, he ?

    
risposta data 13.03.2015 - 08:58
fonte
3

A mio avviso, no. Le cose che puoi fare con i flussi sono un superset rigoroso delle cose che puoi fare con le collezioni, e spesso possono essere rese più efficienti, quindi non c'è ragione per non usarle se non per familiarità. "Le espressioni Lambda sono il gateway del farmaco per Java 8, ma gli stream sono la vera dipendenza". (Venkat Subramaniam, Programmazione funzionale in Java )

    
risposta data 12.03.2015 - 20:05
fonte

Leggi altre domande sui tag