182 votes

Dois-je retourner une Collection ou d'un Ruisseau?

Supposons que j'ai une méthode qui retourne une vue en lecture seule dans une liste des membres:

class Team
{
    private List<Player> players = new ArrayList<>();

    // ...

    public List<Player> getPlayers()
    {
        return Collections.unmodifiableList(players);
    }
}

En outre supposer que le client n'est itérer sur la liste une fois, immédiatement. Peut-être pour mettre les joueurs dans une JList ou quelque chose. Le client n'a pas stocker une référence à la liste pour plus tard inspection!

Compte tenu de ce scénario, dois-je retourner un flux à la place?

    public Stream<Player> getPlayers()
    {
        return players.stream();
    }

Ou est le retour d'un flux non-idiomatiques en Java? Ont été les flux conçu pour être toujours "mis fin" à l'intérieur de la même expression qu'ils ont été créés?

252voto

Brian Goetz Points 6062

La réponse est, comme toujours, "ça dépend". Cela dépend de la collection retournée sera. Cela dépend si les changements au fil du temps, et de l'importance de la cohérence du résultat renvoyé est. Et cela dépend beaucoup de la façon dont l'utilisateur est susceptible d'utiliser la réponse.

Tout d'abord, notez que vous pouvez toujours obtenir une Collection à partir d'un Flux, et vice-versa:

// If API returns Collection, convert with stream()
getFoo().stream()...

// If API returns Stream, use collect()
Collection<T> c = getFooStream().collect(toList());

Donc la question est, qui est plus utile à vos visiteurs.

Si votre résultat peut être infinie, il n'y a qu'un seul choix: le Flux.

Si votre résultat peut être très grande, vous préférez sans doute Flux, puisqu'il peut ne pas être n'importe quelle valeur à la concrétisation de tout cela à la fois, et cela pourrait créer des tas de pression.

Si tous les appelant va faire est de parcourir (de recherche, de filtrage, d'agrégation), vous devriez préférer les Flux, depuis le Ruisseau a ces déjà et il n'y a pas besoin de matérialiser une collection (surtout si l'utilisateur ne pourrait pas traiter la totalité du résultat.) C'est un cas très commun.

Même si vous savez que l'utilisateur va se répéter plusieurs fois ou au contraire le garder, vous pouvez toujours retourner un Flux au lieu de cela, pour le simple fait que, quelle que soit Collection que vous choisissez de le placer dans (p. ex., liste de tableaux) ne peut pas être la forme qu'ils veulent, et puis l'appelant a copier de toute façon. si vous retournez un flux, qu'ils peuvent faire, collect(toCollection(factory)) et obtenir exactement la forme qu'ils veulent.

Ci-dessus "préfèrent Stream" le cas la plupart découler du fait que le Flux est plus flexible; vous pouvez en retard lié à la façon dont vous l'utiliser sans avoir à supporter les coûts et les contraintes de matérialiser, à une Collection.

Le seul cas où vous devez retourner une Collection, c'est quand il y a de fortes exigences de cohérence, et que vous devez produire une sauvegarde instantanée d'une cible en mouvement. Ensuite, vous aurez envie de mettre les éléments dans une collection qui ne changera pas.

Donc, je dirais que la plupart du temps, le Flux est la bonne réponse (il est plus souple, il n'impose pas de d'habitude-inutile matérialisation des coûts, et peut être facilement transformé en la Collection de votre choix si nécessaire. Mais parfois, vous pourriez avoir à retourner une Collection (par exemple, en raison des fortes exigences de cohérence), ou vous souhaitez peut-être le retour de la Collection parce que vous savez comment l'utilisateur va utiliser et savent que c'est la chose la plus commode pour eux.

73voto

Stuart Marks Points 8927

J'ai quelques points à ajouter à Brian Goetz " excellente réponse.

Il est assez courant de retour d'un Flux à partir d'un "getter" style de l'appel de méthode. Voir l' utilisation de Flux de la page , dans le Java 8 javadoc et de chercher des "méthodes... que les Flux retour" pour les emballages autres que java.util.Stream. Ces méthodes sont généralement sur les classes qui représentent ou peuvent contenir plusieurs valeurs ou des agrégations de quelque chose. Dans de tels cas, les Api ont généralement retournés ou des collections de tableaux. Pour toutes les raisons que Brian a fait remarquer dans sa réponse, il est très souple, afin d'ajouter les Flux de retour des méthodes ici. La plupart de ces classes ont des collections ou de la matrice de retour des méthodes déjà, parce que les classes sont antérieurs à l'API de Flux. Si vous êtes en train de concevoir une nouvelle API, et il est logique de prévoir les Flux de retour des méthodes, il pourrait ne pas être nécessaire d'ajouter de la collection de revenir à des méthodes aussi bien.

Brian mentionne le coût de "matérialiser" les valeurs dans une collection. Pour amplifier ce point, il y a en fait deux coûts: le coût de stocker des valeurs dans la collection (allocation de mémoire et à la reproduction) et aussi le coût de la création de valeurs dans la première place. Le dernier coût peuvent souvent être réduits ou évités en prenant avantage d'un Flux de paresse les comportements de recherche. Un bon exemple de ceci sont les Api en java.nio.file.Files:

static Stream<String>  lines(path)
static List<String>    readAllLines(path)

Non seulement est - readAllLines de détenir la totalité du contenu du fichier en mémoire en vue de la stocker dans la liste des résultats, il faut aussi lire le fichier jusqu'à la fin, avant qu'il retourne la liste. L' lines méthode peut revenir, presque immédiatement après, il a effectué une partie de l'installation, en laissant de lecture du fichier et la ligne de rupture jusqu'à ce que plus tard, lorsque c'est nécessaire-ou pas du tout. C'est un énorme avantage, si par exemple, l'appelant ne s'intéresse qu'à la première d'une dizaine de lignes:

List<String> firstTen = Files.lines(path).limit(10).collect(toList());

Bien sûr considérable de l'espace mémoire peut être sauvé si l'appelant filtre les flux de retour seules les lignes correspondant à un modèle, etc.

Un idiome qui semble se dégager est le nom de flux de retour des méthodes après le pluriel du nom des choses qu'il représente ou contient, sans get préfixe. Aussi, tandis que d' stream() est raisonnable de nom pour un flux de retour de la méthode quand il n'est qu'un ensemble de valeurs de retour, que parfois il y a des classes qui ont des agrégations de plusieurs types de valeurs. Par exemple, supposons que vous avez un objet qui contient à la fois les attributs et les éléments. Vous pouvez fournir deux flux de retour d'Api:

Stream<Attribute>  attributes();
Stream<Element>    elements();

2voto

Peter Lawrey Points 229686

Ont été les flux conçu pour être toujours "mis fin" à l'intérieur de la même expression qu'ils ont été créés?

C'est la façon dont ils sont utilisés dans la plupart des exemples.

Remarque: de retour d'un Flux n'est pas différente de retour d'un Itérateur (admis avec beaucoup plus de force expressive)

À mon humble avis, la meilleure solution consiste à encapsuler, pourquoi vous faites ceci, et pas de retour de la collection.

par exemple

public int playerCount();
public Player player(int n);

ou si vous avez l'intention de les compter

public int countPlayersWho(Predicate<? super Player> test);

0voto

gontard Points 5097

Je pense que cela dépend de votre scénario. Peut-être, si vous faites votre Team œuvre Iterable<Player>, c'est suffisant.

for (Player player : team) {
    System.out.println(player);
}

ou dans un style fonctionnel:

team.forEach(System.out::println);

Mais si vous voulez une présentation plus complète et api fluent, un flux pourrait être une bonne solution.

-5voto

dkatzel Points 9062

J'aurais probablement 2 méthodes, l'une pour retourner un Collection et un retour à la collection en tant que Stream.

class Team
{
    private List<Player> players = new ArrayList<>();

// ...

    public List<Player> getPlayers()
    {
        return Collections.unmodifiableList(players);
    }

    public Stream<Player> getPlayerStream()
    {
        return players.stream();
    }

}

C'est le meilleur des deux mondes. Le client peut choisir si elles veulent de la Liste ou de la rivière et ils n'ont pas à faire l'objet supplémentaire de la création de la fabrication d'une immuable copie de la liste juste pour obtenir un Flux de données.

Cela aussi, ne ajoute 1 plus de la méthode de votre API de sorte que vous n'avez pas trop de méthodes

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X