Cómo hacer una nueva lista con una propiedad de un objeto que está en otra lista

81

Imagina que tengo una lista de ciertos objetos:

List<Student>

Y necesito generar otra lista que incluya la idsde Studentsen la lista anterior:

List<Integer>

Evitando usar un bucle, ¿es posible lograr esto usando colecciones de apache o guayaba ?

¿Qué métodos deberían ser útiles para mi caso?

Javatar
fuente
Hola, lo encontré hace un momento: stackoverflow.com/questions/737244/…
Javatar
La respuesta de Jon es bastante buena, pero si la miras, también usa un bucle. Cualquier solución usará un bucle, aunque puede que no sea visible para usted, pero internamente lo hará
hage
alguien tiene que aplicar un bucle para hacerlo. ya sea usted o alguna lib que pueda usar.
Sudmong
En realidad, la respuesta de Jon no se ajusta a mi situación, ya que no quiero usar for loop. Creo que hay una forma más conveniente de ir a algún lado, pero
espero

Respuestas:

175

Java 8 forma de hacerlo: -

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Kaushik
fuente
2
¿Qué pasa si quiero una lista de tuplas? Algo como [(Id, nombre), (Id, nombre)].
Coder95
No todas las funciones 1.8 son compatibles con todas las API de Android. En particular, java.util.stream no es compatible hasta API 24.
The Black Horse
41

Con Guava puedes usar funciones como -

private enum StudentToId implements Function<Student, Integer> {
        INSTANCE;

        @Override
        public Integer apply(Student input) {
            return input.getId();
        }
    }

y puede usar esta función para convertir la Lista de estudiantes a identificadores como -

Lists.transform(studentList, StudentToId.INSTANCE);

Seguramente se repetirá para extraer todos los identificadores, pero recuerde que los métodos de guayaba devuelven la vista y la función solo se aplicará cuando intente iterar sobre el List<Integer>
Si no itera, nunca aplicará el ciclo.

Nota: Recuerde que esta es la vista y si desea iterar varias veces, será mejor copiar el contenido en alguna otra List<Integer>como

ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Premraj
fuente
Creo que no es elegante tener que definir una función por separado. Creo que esto también se puede hacer de forma anónima y en un proceso de un solo paso
Marc
¿Cómo se extraen los miembros de cadena (flotante, etc.) de la Lista?
septiembre
22

Gracias a Premraj por la opción genial alternativa, upvoted.

He utilizado apache CollectionUtils y BeanUtils. En consecuencia, estoy satisfecho con el rendimiento del siguiente código:

List<Long> idList = (List<Long>) CollectionUtils.collect(objectList, 
                                    new BeanToPropertyValueTransformer("id"));

Vale la pena mencionar que compararé el rendimiento de la guayaba ( proporcionada por Premraj ) y collectionUtils que usé anteriormente, y decidiré cuál es la más rápida.

Javatar
fuente
3
Preferiré Guava sobre las colecciones de apache independientemente del rendimiento por razones como: más moderno, genérico, no usa la reflexión para las transformaciones, etc. En general, la guayaba es mucho más moderna que las colecciones de apache - lea más aquí
Premraj
1
Sí, pero en realidad no me parece una buena idea restringir su uso por la necesidad obligatoria de una declaración de enumeración adicional para cada tipo de objeto que quiero transformar.
Javatar
Bueno, depende de su diseño: puede hacer que la interfaz diga Identifiablequé getId()método define y luego puede usar este patrón singleton de enumeración única para extraer Id comúnmente.
Premraj
2
guayaba es definitivamente más eficiente que Bean utils ya que el último usa reflejos ..
ravi
2
Usar el nombre de la propiedad como una Cadena en el constructor new BeanToPropertyValueTransformer ("id") es algo que definitivamente trato de evitar. ¡Refactorizar será difícil! Pero estoy buscando una solución para solucionar esto. Mientras tanto, optaré por la solución de guayaba, detallada pero segura.
redochka
7

Solución de expresión lambda de Java 8:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
Opster Elasticsearch Pro-Vijay
fuente
Si Java <1.8, utilice Apache CollectionUtils. Ejemplo: Collection <String> firstnames = CollectionUtils.collect (lista de usuarios, TransformerUtils.invokerTransformer ("getFirstname"));
a_subscriber
5

Si alguien llega después de unos años:

List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
Jonathan Perrotta
fuente
Y acabas de salvarme :)
Behrad3d
-5

Es matemáticamente imposible hacer esto sin un bucle. Para crear un mapeo, F, de un conjunto discreto de valores a otro conjunto discreto de valores, F debe operar en cada elemento del conjunto de origen. (Se requiere un bucle para hacer esto, básicamente).

Habiendo dicho eso:

¿Por qué necesita una nueva lista? Podría estar abordando cualquier problema que esté resolviendo de manera incorrecta.

Si tiene una lista de Student, entonces está a solo uno o dos pasos de distancia, cuando recorra esta lista, de iterar sobre los números de identificación de los estudiantes.

for(Student s : list)
{
    int current_id = s.getID();
    // Do something with current_id
}

Si tiene otro tipo de problema, entonces comente / actualice la pregunta e intentaremos ayudarlo.

Zéychin
fuente
Primero gracias por tu respuesta. Sin embargo, es dramático que haya dicho que es incorrecto necesitar una nueva lista. Porque depende de la capacidad y alcance de la necesidad. Por lo tanto, su enfoque es el camino equivocado :) Nuevamente, mi mención sobre "la capacidad de lograr eso sin bucle" se refiere a una forma más conveniente para no hacer que el código sea ilegible y de bajo rendimiento. De todos modos, ya encontré la forma de lograrlo, lo voy a compartir un poco.
Javatar
No dije que sea la forma incorrecta, pero podría ser la forma incorrecta, dependiendo del problema que esté tratando de resolver.
Zéychin