147 votos

Debo devolver una Colección o de un Arroyo?

Supongamos que tengo un método que devuelve una vista de sólo lectura en una lista de miembros:

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

    // ...

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

Suponga que todos los que hace el cliente es iterar sobre la lista de una vez, inmediatamente. Tal vez para poner a los jugadores en un JList o algo. El cliente no almacenar una referencia a la lista para su posterior inspección.

Dado este escenario común, debería devolver una secuencia en su lugar?

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

O está regresando de un flujo no-idiomáticas en Java? Fueron los arroyos diseñadas para ser siempre "terminado" en el interior de la misma expresión que fueron creados?

201voto

Brian Goetz Puntos 6062

La respuesta es, como siempre, "depende". Depende de lo grande que es el devuelto colección será. Depende de si el resultado de los cambios a lo largo del tiempo, y lo importante de la consistencia del resultado devuelto es. Y de esto depende mucho de cómo el usuario es probable que el uso de la respuesta.

En primer lugar, tenga en cuenta que usted puede conseguir siempre una Colección a partir de una Secuencia, y viceversa:

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

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

Así que la pregunta es, que es más útil para las personas que llaman.

Si el resultado podría ser infinita, sólo hay una opción: Secuencia.

Si el resultado puede ser muy grande, probablemente prefiera Corriente, ya que no puede ser cualquier valor de materializar todo de una vez, y si lo hace, podría crear significativo montón de presión.

Si todas las llamadas se va a hacer es recorrer a través de ella (buscar, filtrar, agregar), se debe preferir Corriente, ya que la emisión de estos incorporado ya, y no hay necesidad de materializar una colección (especialmente si el usuario no podría procesar todo el resultado.) Este es un caso muy común.

Incluso si usted sabe que el usuario se repite varias veces o mantenga alrededor, usted todavía desea, puede devolver una Secuencia en su lugar, por el simple hecho de que, independientemente de la Colección que usted elige poner en (por ejemplo, ArrayList) puede no ser la forma que desee y, a continuación, la persona que llama tiene que copiar todos modos. si usted devuelve un stream, que pueden hacer collect(toCollection(factory)) y obtener exactamente de la forma que ellos quieren.

Los de arriba "prefieren Stream" la mayoría de los casos se derivan del hecho de que la Corriente es más flexible; puede que tarde-se unen a la forma de usarlo sin incurrir en los costos y limitaciones de materializar, a una Colección.

El único caso en el cual debe devolver una Colección es cuando hay fuertes requisitos de coherencia, y usted tiene que producir una consistente instantánea de un objeto en movimiento. A continuación, usted querrá poner los elementos en una colección que no va a cambiar.

Así que yo diría que la mayoría del tiempo, la Corriente es la respuesta correcta, ya que es más flexible, no imponer generalmente-no es necesaria la materialización de los costos, y puede ser fácilmente convertido en la Recogida de su elección, si es necesario. Pero a veces, puede que tenga que devolver una Colección (por ejemplo, debido a los fuertes requisitos de coherencia), o es posible que desee volver Colección, porque usted sabe cómo el usuario va a usar y saber que esto es lo más conveniente para ellos.

62voto

Stuart Marks Puntos 8927

Tengo un par de puntos a añadir a Brian Goetz' excelente respuesta.

Es muy común para devolver una Secuencia de un "captador" de estilo de la llamada al método. Ver el uso de la Secuencia de la página en el Java 8 javadoc y buscar "métodos... que la Secuencia de retorno" para los paquetes de java.util.Stream. Estos métodos son por lo general en las clases que representan o pueden contener varios valores o agregaciones de algo. En tales casos, Api normalmente han vuelto colecciones o conjuntos de ellos. Por todas las razones que Brian señaló en su respuesta, es muy flexible para añadir Arroyo-la devolución de los métodos de aquí. Muchas de estas clases tienen colecciones - o matriz-la devolución de los métodos ya, porque las clases son anteriores a los Flujos de API. Si estás diseñando una nueva API, y tiene sentido para proporcionar Flujo de regresar métodos, puede que no sea necesario agregar colección-la devolución de los métodos.

Brian se menciona el costo de la "materialización" de los valores en una colección. Para amplificar este punto, en realidad, hay dos tipos de costos: el costo de almacenamiento de los valores de la colección (asignación de memoria y copia) y también el costo de la creación de los valores en el primer lugar. El costo a menudo puede reducirse o evitarse tomando ventaja de una Corriente pereza del comportamiento de búsqueda. Un buen ejemplo de esto son las APIs en java.nio.file.Files:

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

No sólo readAllLines tiene para contener todo el contenido del archivo en la memoria para almacenarlo en la lista de resultados, también ha de leer el archivo hasta el final antes de que se devuelve la lista. El lines método puede devolver casi inmediatamente después de que se ha realizado algún tipo de configuración, dejando la lectura de archivos y los saltos de línea hasta más tarde cuando es necesario, o no del todo. Este es un gran beneficio, si por ejemplo, la persona que llama está interesado sólo en las primeras diez líneas:

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

Por supuesto un considerable espacio de memoria puede ser salvado si la persona que llama filtros de la corriente a devolver sólo las líneas que coincidan con un patrón, etc.

De un lenguaje que parece estar emergiendo es el nombre de arroyo-la devolución de los métodos después de que el plural del nombre de las cosas que se representa o contiene, sin un get prefijo. También, mientras stream() es un nombre razonable para un flujo de método de devolución cuando sólo hay un posible conjunto de valores a devolverse, a veces hay clases que tienen las agregaciones de varios tipos de valores. Por ejemplo, supongamos que tenemos un objeto que contiene los atributos y elementos. Usted puede proporcionar dos stream-la devolución de Api:

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

1voto

Peter Lawrey Puntos 229686

Fueron los arroyos diseñadas para ser siempre "terminado" en el interior de la misma expresión que fueron creados?

Que es la forma en que se utilizan en la mayoría de los ejemplos.

Nota: la devolución de un Flujo no es muy diferente a la devolución de un Iterador (admitidos con mucho más poder expresivo)

En mi humilde opinión, la mejor solución es resumir por qué usted está haciendo esto, y no volver a la colección.

por ejemplo,

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

o si usted tiene la intención de contar

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

0voto

gontard Puntos 5097

Yo creo que depende de tu escenario. Puede ser, si usted hace su Team implementar Iterable<Player>, es suficiente.

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

o en el estilo funcional:

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

Pero si usted desea un más completo y fluido de la api, una corriente podría ser una buena solución.

-5voto

dkatzel Puntos 9062

Probablemente tengo 2 métodos, uno para devolver un Collection y para devolver a la colección como Stream.

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

// ...

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

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

}

Este es el mejor de ambos mundos. El cliente puede elegir si se desea que la Lista o la Secuencia y que no tienen que hacer el extra de creación de objeto de hacer una inmutable copia de la lista sólo para obtener un Arroyo.

Esto también sólo añade 1 método más a su API para que usted no tenga demasiados métodos

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X