Pregunta

quiero filtrar un java.util.Collection basado en un predicado.

¿Fue útil?

Solución

Java 8 (2014) resuelve este problema usando transmisiones y lambdas en una línea de código:

List<Person> beerDrinkers = persons.stream()
    .filter(p -> p.getAge() > 16).collect(Collectors.toList());

Aquí está un tutorial.

Usar Collection#removeIf para modificar la colección existente.(Aviso:En este caso, el predicado eliminará los objetos que cumplan el predicado):

persons.removeIf(p -> p.getAge() <= 16);

lambdaj permite filtrar colecciones sin escribir bucles o clases internas:

List<Person> beerDrinkers = select(persons, having(on(Person.class).getAge(),
    greaterThan(16)));

¿Te imaginas algo más legible?

Descargo de responsabilidad: Soy colaborador de lambdaj.

Otros consejos

Suponiendo que esté utilizando Java 1.5, y que no puedes agregar Colecciones de Google, yo haría algo muy parecido a lo que hicieron los chicos de Google.Esta es una ligera variación de los comentarios de Jon.

Primero agregue esta interfaz a su código base.

public interface IPredicate<T> { boolean apply(T type); }

Sus implementadores pueden responder cuando un determinado predicado es verdadero para un determinado tipo.P.ej.Si T eran User y AuthorizedUserPredicate<User> implementos IPredicate<T>, entonces AuthorizedUserPredicate#apply devuelve si lo pasado User está autorizado.

Luego, en alguna clase de utilidad, se podría decir

public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
    Collection<T> result = new ArrayList<T>();
    for (T element: target) {
        if (predicate.apply(element)) {
            result.add(element);
        }
    }
    return result;
}

Entonces, asumiendo que tienes el uso de lo anterior podría ser

Predicate<User> isAuthorized = new Predicate<User>() {
    public boolean apply(User user) {
        // binds a boolean method in User to a reference
        return user.isAuthorized();
    }
};
// allUsers is a Collection<User>
Collection<User> authorizedUsers = filter(allUsers, isAuthorized);

Si le preocupa el rendimiento en la verificación lineal, es posible que desee tener un objeto de dominio que tenga la colección de destino.El objeto de dominio que tiene la colección de destino tendría una lógica de filtrado para los métodos que inicializan, agregan y configuran la colección de destino.

ACTUALIZAR:

En la clase de utilidad (digamos Predicado), agregué un método de selección con una opción para el valor predeterminado cuando el predicado no devuelve el valor esperado, y también una propiedad estática para que los parámetros se usen dentro del nuevo IPredicate.

public class Predicate {
    public static Object predicateParams;

    public static <T> Collection<T> filter(Collection<T> target, IPredicate<T> predicate) {
        Collection<T> result = new ArrayList<T>();
        for (T element : target) {
            if (predicate.apply(element)) {
                result.add(element);
            }
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate) {
        T result = null;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }

    public static <T> T select(Collection<T> target, IPredicate<T> predicate, T defaultValue) {
        T result = defaultValue;
        for (T element : target) {
            if (!predicate.apply(element))
                continue;
            result = element;
            break;
        }
        return result;
    }
}

El siguiente ejemplo busca objetos faltantes entre colecciones:

List<MyTypeA> missingObjects = (List<MyTypeA>) Predicate.filter(myCollectionOfA,
    new IPredicate<MyTypeA>() {
        public boolean apply(MyTypeA objectOfA) {
            Predicate.predicateParams = objectOfA.getName();
            return Predicate.select(myCollectionB, new IPredicate<MyTypeB>() {
                public boolean apply(MyTypeB objectOfB) {
                    return objectOfB.getName().equals(Predicate.predicateParams.toString());
                }
            }) == null;
        }
    });

El siguiente ejemplo busca una instancia en una colección y devuelve el primer elemento de la colección como valor predeterminado cuando no se encuentra la instancia:

MyType myObject = Predicate.select(collectionOfMyType, new IPredicate<MyType>() {
public boolean apply(MyType objectOfMyType) {
    return objectOfMyType.isDefault();
}}, collectionOfMyType.get(0));

ACTUALIZACIÓN (después del lanzamiento de Java 8):

Han pasado varios años desde que yo (Alan) publiqué esta respuesta por primera vez y todavía no puedo creer que esté acumulando SO puntos por esta respuesta.En cualquier caso, ahora que Java 8 ha introducido cierres en el lenguaje, mi respuesta sería considerablemente diferente y más sencilla.Con Java 8, no hay necesidad de una clase de utilidad estática distinta.Entonces, si desea encontrar el primer elemento que coincida con su predicado.

final UserService userService = ... // perhaps injected IoC
final Optional<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).findFirst();

La API JDK 8 para opcionales tiene la capacidad de get(), isPresent(), orElse(defaultUser), orElseGet(userSupplier) y orElseThrow(exceptionSupplier), así como otras funciones 'monádicas' como map, flatMap y filter.

Si desea simplemente recopilar todos los usuarios que coinciden con el predicado, utilice el Collectors para finalizar la transmisión en la colección deseada.

final UserService userService = ... // perhaps injected IoC
final List<UserModel> userOption = userCollection.stream().filter(u -> {
    boolean isAuthorized = userService.isAuthorized(u);
    return isAuthorized;
}).collect(Collectors.toList());

Ver aquí para obtener más ejemplos sobre cómo funcionan las transmisiones de Java 8.

La "mejor" forma es una solicitud demasiado amplia.¿Es "el más corto"?"Lo más rápido"?"Legible"?¿Filtrar en su lugar o en otra colección?

La forma más sencilla (pero no la más legible) es iterarlo y utilizar el método Iterator.remove():

Iterator<Foo> it = col.iterator();
while( it.hasNext() ) {
  Foo foo = it.next();
  if( !condition(foo) ) it.remove();
}

Ahora, para hacerlo más legible, puedes incluirlo en un método de utilidad.Luego invente una interfaz IPredicate, cree una implementación anónima de esa interfaz y haga algo como:

CollectionUtils.filterInPlace(col,
  new IPredicate<Foo>(){
    public boolean keepIt(Foo foo) {
      return foo.isBar();
    }
  });

donde filterInPlace() itera la colección y llama a Predicate.keepIt() para saber si la instancia se mantendrá en la colección.

Realmente no veo una justificación para incorporar una biblioteca de terceros solo para esta tarea.

Considerar Colecciones de Google para un marco de Colecciones actualizado que admita genéricos.

ACTUALIZAR:La biblioteca de colecciones de Google ahora está en desuso.Debe utilizar la última versión de Guayaba en cambio.Todavía tiene las mismas extensiones para el marco de colecciones, incluido un mecanismo de filtrado basado en un predicado.

Espere a Java 8:

List<Person> olderThan30 = 
  //Create a Stream from the personList
  personList.stream().
  //filter the element to select only those with age >= 30
  filter(p -> p.age >= 30).
  //put those filtered elements into a new List.
  collect(Collectors.toList());

Desde el lanzamiento inicial de Java 8, puedes probar algo como:

Collection<T> collection = ...;
Stream<T> stream = collection.stream().filter(...);

Por ejemplo, si tuviera una lista de números enteros y quisiera filtrar los números que son > 10 y luego imprimir esos números en la consola, podría hacer algo como:

List<Integer> numbers = Arrays.asList(12, 74, 5, 8, 16);
numbers.stream().filter(n -> n > 10).forEach(System.out::println);

voy a tirar RxJava en el ring, que también está disponible en Androide.Es posible que RxJava no siempre sea la mejor opción, pero le brindará más flexibilidad si desea agregar más transformaciones a su colección o manejar errores durante el filtrado.

Observable.from(Arrays.asList(1, 2, 3, 4, 5))
    .filter(new Func1<Integer, Boolean>() {
        public Boolean call(Integer i) {
            return i % 2 != 0;
        }
    })
    .subscribe(new Action1<Integer>() {
        public void call(Integer i) {
            System.out.println(i);
        }
    });

Producción:

1
3
5

Más detalles sobre RxJava filter puede ser encontrado aquí.

La puesta en marcha:

public interface Predicate<T> {
  public boolean filter(T t);
}

void filterCollection(Collection<T> col, Predicate<T> predicate) {
  for (Iterator i = col.iterator(); i.hasNext();) {
    T obj = i.next();
    if (predicate.filter(obj)) {
      i.remove();
    }
  }
}

El uso:

List<MyObject> myList = ...;
filterCollection(myList, new Predicate<MyObject>() {
  public boolean filter(MyObject obj) {
    return obj.shouldFilter();
  }
});

¿Qué tal algo de Java sencillo y directo?

 List<Customer> list ...;
 List<Customer> newList = new ArrayList<>();
 for (Customer c : list){
    if (c.getName().equals("dd")) newList.add(c);
 }

Simple, legible y fácil (¡funciona en Android!) Pero si estás usando Java 8, puedes hacerlo en una línea dulce:

List<Customer> newList = list.stream().filter(c -> c.getName().equals("dd")).collect(toList());

Tenga en cuenta que toList() se importa estáticamente

¿Está seguro de que desea filtrar la colección en sí, en lugar de un iterador?

ver org.apache.commons.collections.iterators.FilterIterator

o usando la versión 4 de apache commons org.apache.commons.collections4.iterators.FilterIterator

Veamos cómo filtrar una lista JDK incorporada y una Lista mutable usando Colecciones de eclipses (antes Colecciones GS).

List<Integer> jdkList = Arrays.asList(1, 2, 3, 4, 5);
MutableList<Integer> ecList = Lists.mutable.with(1, 2, 3, 4, 5);

Si quisiera filtrar los números menores de 3, esperaría los siguientes resultados.

List<Integer> selected = Lists.mutable.with(1, 2);
List<Integer> rejected = Lists.mutable.with(3, 4, 5);

A continuación se explica cómo puede filtrar utilizando una clase interna anónima como Predicate.

Predicate<Integer> lessThan3 = new Predicate<Integer>()
{
    public boolean accept(Integer each)
    {
        return each < 3;
    }
};

Assert.assertEquals(selected, Iterate.select(jdkList, lessThan3));

Assert.assertEquals(selected, ecList.select(lessThan3));

Aquí hay algunas alternativas para filtrar listas JDK y Colecciones de eclipses Listas mutables usando el Predicados fábrica.

Assert.assertEquals(selected, Iterate.select(jdkList, Predicates.lessThan(3)));

Assert.assertEquals(selected, ecList.select(Predicates.lessThan(3)));

Aquí hay una versión que no asigna un objeto para el predicado, usando el Predicados2 fábrica en su lugar con el selectWith método que requiere un Predicate2.

Assert.assertEquals(
    selected, ecList.selectWith(Predicates2.<Integer>lessThan(), 3));

A veces quieres filtrar por una condición negativa.Hay un método especial en las Colecciones de Eclipse para eso llamado reject.

Assert.assertEquals(rejected, Iterate.reject(jdkList, lessThan3));

Assert.assertEquals(rejected, ecList.reject(lessThan3));

A continuación se explica cómo puede filtrar utilizando una lambda de Java 8 como Predicate.

Assert.assertEquals(selected, Iterate.select(jdkList, each -> each < 3));
Assert.assertEquals(rejected, Iterate.reject(jdkList, each -> each < 3));

Assert.assertEquals(selected, gscList.select(each -> each < 3));
Assert.assertEquals(rejected, gscList.reject(each -> each < 3));

El método partition devolverá dos colecciones, que contienen los elementos seleccionados y rechazados por el Predicate.

PartitionIterable<Integer> jdkPartitioned = Iterate.partition(jdkList, lessThan3);
Assert.assertEquals(selected, jdkPartitioned.getSelected());
Assert.assertEquals(rejected, jdkPartitioned.getRejected());

PartitionList<Integer> ecPartitioned = gscList.partition(lessThan3);
Assert.assertEquals(selected, ecPartitioned.getSelected());
Assert.assertEquals(rejected, ecPartitioned.getRejected());

Nota:Soy un confirmador de las colecciones de Eclipse.

Con ForEach DSL puedes escribir

import static ch.akuhn.util.query.Query.select;
import static ch.akuhn.util.query.Query.$result;
import ch.akuhn.util.query.Select;

Collection<String> collection = ...

for (Select<String> each : select(collection)) {
    each.yield = each.value.length() > 3;
}

Collection<String> result = $result();

Dada una colección de [The, quick, brown, fox, jumps, over, the, lazy, dog] esto da como resultado [quick, brown, jumps, over, lazy], es decir, todas las cadenas de más de tres caracteres.

Todos los estilos de iteración soportados por ForEach DSL son

  • AllSatisfy
  • AnySatisfy
  • Collect
  • Counnt
  • CutPieces
  • Detect
  • GroupedBy
  • IndexOf
  • InjectInto
  • Reject
  • Select

Para obtener más detalles, consulte https://www.iam.unibe.ch/scg/svn_repos/Sources/ForEach

El Colecciones2.filter(Colección,Predicado) método en Biblioteca de guayaba de Google hace justo lo que estás buscando.

Desde Java 9 Collectors.filtering está habilitado:

public static <T, A, R>
    Collector<T, ?, R> filtering(Predicate<? super T> predicate,
                                 Collector<? super T, A, R> downstream)

Por tanto, el filtrado debería ser:

collection.stream().collect(Collectors.filtering(predicate, collector))

Ejemplo:

List<Integer> oddNumbers = List.of(1, 19, 15, 10, -10).stream()
            .collect(Collectors.filtering(i -> i % 2 == 1, Collectors.toList()));

Esto, combinado con la falta de cierres reales, es mi mayor queja sobre Java.Honestamente, la mayoría de los métodos mencionados anteriormente son bastante fáciles de leer y REALMENTE eficientes;sin embargo, después de pasar tiempo con .Net, Erlang, etc...La comprensión de listas integrada a nivel del idioma hace que todo sea mucho más limpio.Sin adiciones a nivel de lenguaje, Java simplemente no puede ser tan limpio como muchos otros lenguajes en esta área.

Si el rendimiento es una gran preocupación, las colecciones de Google son el camino a seguir (o escriba su propia utilidad de predicado simple).La sintaxis Lambdaj es más legible para algunas personas, pero no es tan eficiente.

Y luego hay una biblioteca que escribí.Ignoraré cualquier pregunta con respecto a su eficiencia (sí, es tan malo)...Sí, sé que está claramente basado en reflejos y no, en realidad no lo uso, pero funciona:

LinkedList<Person> list = ......
LinkedList<Person> filtered = 
           Query.from(list).where(Condition.ensure("age", Op.GTE, 21));

O

LinkedList<Person> list = ....
LinkedList<Person> filtered = Query.from(list).where("x => x.age >= 21");

JFiltro http://code.google.com/p/jfilter/ es el más adecuado para sus necesidades.

JFilter es una biblioteca de código abierto simple y de alto rendimiento para consultar colecciones de beans Java.

Características clave

  • Soporte de propiedades de colección (java.util.Collection, java.util.Map y Array).
  • Soporte de colección dentro de colección de cualquier profundidad.
  • Soporte de consultas internas.
  • Soporte de consultas parametrizadas.
  • Puede filtrar 1 millón de registros en unos 100 ms.
  • El filtro (consulta) se proporciona en formato json simple, es como las consultas de Mangodb.A continuación se muestran algunos ejemplos.
  • { "id":{"$le":"10"}
    • donde la propiedad de identificación del objeto es menor que 10.
  • { "identificación":{"$en":["0", "100"]}}
    • donde la propiedad de identificación del objeto es 0 o 100.
  • {"lineItems":{"lineAmount":"1"}}
    • donde la propiedad de colección lineItems del tipo parametrizado tiene lineAmount igual a 1.
  • { "$y":[{"id":"0"}, {"billingAddress":{"ciudad":"DEL"}}]}
    • donde la propiedad id es 0 y la propiedad billingAddress.city es DEL.
  • {"lineItems":{"impuestos":{ "clave":{"código":"GST"}, "valor":{"$gt":"1.01"}}}}
    • donde la propiedad de colección lineItems de tipo parametrizado que tiene la propiedad de tipo de mapa de impuestos de tipo parametrizado tiene un código igual a un valor GST mayor que 1,01.
  • {'$o':[{'código':'10'},{'skus':{'$y':[{'precio':{'$en':['20', '40']}}, {'código':'RedApple'}]}}]}
    • Seleccione todos los productos donde el código de producto sea 10 o el precio de SKU en 20 y 40 y el código de SKU sea "RedApple".

escribí una clase Iterable extendida que admiten la aplicación de algoritmos funcionales sin copiar el contenido de la colección.

Uso:

List<Integer> myList = new ArrayList<Integer>(){ 1, 2, 3, 4, 5 }

Iterable<Integer> filtered = Iterable.wrap(myList).select(new Predicate1<Integer>()
{
    public Boolean call(Integer n) throws FunctionalException
    {
        return n % 2 == 0;
    }
})

for( int n : filtered )
{
    System.out.println(n);
}

El código anterior realmente se ejecutará

for( int n : myList )
{
    if( n % 2 == 0 ) 
    {
        System.out.println(n);
    }
}

Algunas respuestas realmente geniales aquí.A mí me gustaría mantener las cosas lo más simples y legibles posible:

public abstract class AbstractFilter<T> {

    /**
     * Method that returns whether an item is to be included or not.
     * @param item an item from the given collection.
     * @return true if this item is to be included in the collection, false in case it has to be removed.
     */
    protected abstract boolean excludeItem(T item);

    public void filter(Collection<T> collection) {
        if (CollectionUtils.isNotEmpty(collection)) {
            Iterator<T> iterator = collection.iterator();
            while (iterator.hasNext()) {
                if (excludeItem(iterator.next())) {
                    iterator.remove();
                }
            }
        }
    }
}

La solución simple anterior a Java8:

ArrayList<Item> filtered = new ArrayList<Item>(); 
for (Item item : items) if (condition(item)) filtered.add(item);

Desafortunadamente, esta solución no es completamente genérica y genera una lista en lugar del tipo de colección dada.Además, incorporar bibliotecas o escribir funciones que envuelvan este código me parece excesivo a menos que la condición sea compleja, pero entonces puedes escribir una función para la condición.

https://code.google.com/p/joquery/

Admite diferentes posibilidades,

Colección dada,

Collection<Dto> testList = new ArrayList<>();

de tipo,

class Dto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

Filtrar

Java 7

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property("id").eq().value(1);
Collection<Dto> filtered = query.list();

Java 8

Filter<Dto> query = CQ.<Dto>filter(testList)
    .where()
    .property(Dto::getId)
    .eq().value(1);
Collection<Dto> filtered = query.list();

También,

Filter<Dto> query = CQ.<Dto>filter()
        .from(testList)
        .where()
        .property(Dto::getId).between().value(1).value(2)
        .and()
        .property(Dto::grtText).in().value(new string[]{"a","b"});

Clasificación (también disponible para Java 7)

Filter<Dto> query = CQ.<Dto>filter(testList)
        .orderBy()
        .property(Dto::getId)
        .property(Dto::getName)
    Collection<Dto> sorted = query.list();

Agrupamiento (también disponible para Java 7)

GroupQuery<Integer,Dto> query = CQ.<Dto,Dto>query(testList)
        .group()
        .groupBy(Dto::getId)
    Collection<Grouping<Integer,Dto>> grouped = query.list();

Uniones (también disponible para Java 7)

Dado,

class LeftDto
{
    private int id;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getText()
    {
        return text;
    }
}

class RightDto
{
    private int id;
    private int leftId;
    private String text;

    public int getId()
    {
        return id;
    }

    public int getLeftId()
        {
            return leftId;
        }

    public int getText()
    {
        return text;
    }
}

class JoinedDto
{
    private int leftId;
    private int rightId;
    private String text;

    public JoinedDto(int leftId,int rightId,String text)
    {
        this.leftId = leftId;
        this.rightId = rightId;
        this.text = text;
    }

    public int getLeftId()
    {
        return leftId;
    }

    public int getRightId()
        {
            return rightId;
        }

    public int getText()
    {
        return text;
    }
}

Collection<LeftDto> leftList = new ArrayList<>();

Collection<RightDto> rightList = new ArrayList<>();

Se puede unir como,

Collection<JoinedDto> results = CQ.<LeftDto, LeftDto>query().from(leftList)
                .<RightDto, JoinedDto>innerJoin(CQ.<RightDto, RightDto>query().from(rightList))
                .on(LeftFyo::getId, RightDto::getLeftId)
                .transformDirect(selection ->  new JoinedDto(selection.getLeft().getText()
                                                     , selection.getLeft().getId()
                                                     , selection.getRight().getId())
                                 )
                .list();

Expresiones

Filter<Dto> query = CQ.<Dto>filter()
    .from(testList)
    .where()
    .exec(s -> s.getId() + 1).eq().value(2);

Mi respuesta se basa en la de Kevin Wong, aquí como una sola línea usando CollectionUtils de primavera y un Java 8 lambda expresión.

CollectionUtils.filter(list, p -> ((Person) p).getAge() > 16);

Esto es tan conciso y legible como cualquier alternativa que haya visto (sin usar bibliotecas basadas en aspectos)

Primavera ColecciónUtils está disponible desde la versión Spring 4.0.2.RELEASE, y recuerde que necesita JDK 1.8 y nivel de idioma 8+.

Usando java 8, específicamente lambda expression, puedes hacerlo simplemente como en el siguiente ejemplo:

myProducts.stream().filter(prod -> prod.price>10).collect(Collectors.toList())

donde para cada product adentro myProducts colección, si prod.price>10, luego agregue este producto a la nueva lista filtrada.

Necesitaba filtrar una lista según los valores que ya estaban presentes en la lista.Por ejemplo, elimine todos los valores siguientes que sean menores que el valor actual.{2 5 3 4 7 5} -> {2 5 7}.O, por ejemplo, para eliminar todos los duplicados {3 5 4 2 3 5 6} -> {3 5 4 2 6}.

public class Filter {
    public static <T> void List(List<T> list, Chooser<T> chooser) {
        List<Integer> toBeRemoved = new ArrayList<>();
        leftloop:
        for (int right = 1; right < list.size(); ++right) {
            for (int left = 0; left < right; ++left) {
                if (toBeRemoved.contains(left)) {
                    continue;
                }
                Keep keep = chooser.choose(list.get(left), list.get(right));
                switch (keep) {
                    case LEFT:
                        toBeRemoved.add(right);
                        continue leftloop;
                    case RIGHT:
                        toBeRemoved.add(left);
                        break;
                    case NONE:
                        toBeRemoved.add(left);
                        toBeRemoved.add(right);
                        continue leftloop;
                }
            }
        }

        Collections.sort(toBeRemoved, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

        for (int i : toBeRemoved) {
            if (i >= 0 && i < list.size()) {
                list.remove(i);
            }
        }
    }

    public static <T> void List(List<T> list, Keeper<T> keeper) {
        Iterator<T> iterator = list.iterator();
        while (iterator.hasNext()) {
            if (!keeper.keep(iterator.next())) {
                iterator.remove();
            }
        }
    }

    public interface Keeper<E> {
        boolean keep(E obj);
    }

    public interface Chooser<E> {
        Keep choose(E left, E right);
    }

    public enum Keep {
        LEFT, RIGHT, BOTH, NONE;
    }
}

Esto se usará así.

List<String> names = new ArrayList<>();
names.add("Anders");
names.add("Stefan");
names.add("Anders");
Filter.List(names, new Filter.Chooser<String>() {
    @Override
    public Filter.Keep choose(String left, String right) {
        return left.equals(right) ? Filter.Keep.LEFT : Filter.Keep.BOTH;
    }
});

Con guayaba:

Collection<Integer> collection = Lists.newArrayList(1, 2, 3, 4, 5);

Iterators.removeIf(collection.iterator(), new Predicate<Integer>() {
    @Override
    public boolean apply(Integer i) {
        return i % 2 == 0;
    }
});

System.out.println(collection); // Prints 1, 3, 5

En Java 8, puede utilizar directamente este método de filtro y luego hacerlo.

 List<String> lines = Arrays.asList("java", "pramod", "example");

 List<String> result = lines.stream()              
         .filter(line -> !"pramod".equals(line))     
         .collect(Collectors.toList());              

 result.forEach(System.out::println); 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top