Jak utworzyć nową listę z właściwością obiektu znajdującego się na innej liście

81

Wyobraź sobie, że mam listę pewnych obiektów:

List<Student>

Muszę wygenerować kolejną listę zawierającą elementy idsz Studentspowyższej listy:

List<Integer>

Unikając pętli, czy można to osiągnąć za pomocą kolekcji apache lub guawy ?

Które metody powinny być przydatne w moim przypadku?

Javatar
źródło
Hej, znalazłem to właśnie teraz: stackoverflow.com/questions/737244/…
Javatar
Odpowiedź Jona jest całkiem fajna, ale jeśli spojrzysz na nią, również używa pętli. Każde rozwiązanie wykorzysta pętlę - chociaż może nie być dla ciebie widoczna, ale wewnętrznie będzie
hage
ktoś musi zastosować pętlę, aby to zrobić. Ty lub jakaś biblioteka, której możesz użyć.
sudmong
Właściwie odpowiedź jona nie pasuje do mojej sytuacji, ponieważ nie chcę używać pętli for. Myślę, że jest gdzieś wygodniejszy sposób, ale czekam, aż go znajdę :)
Javatar

Odpowiedzi:

175

Java 8 jak to zrobić: -

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Kaushik
źródło
2
A jeśli chcę listę krotek? Coś w rodzaju [(Id, imię), (Id, imię)].
Coder95,
Nie wszystkie funkcje wersji 1.8 są obsługiwane dla wszystkich interfejsów API systemu Android. W szczególności java.util.stream nie jest obsługiwany do API 24.
The Black Horse
41

Dzięki guawie możesz użyć funkcji, takich jak -

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

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

i możesz użyć tej funkcji do konwersji listy uczniów na identyfikatory, takie jak -

Lists.transform(studentList, StudentToId.INSTANCE);

Z pewnością zapętli się, aby wyodrębnić wszystkie identyfikatory, ale pamiętaj, że metody guawy zwracają widok, a funkcja zostanie zastosowana tylko wtedy, gdy spróbujesz iterować po List<Integer>
pętli Jeśli nie wykonasz iteracji, nigdy nie zastosuje pętli.

Uwaga: Pamiętaj, to jest widok, a jeśli chcesz iteracyjne wiele razy, że będzie lepiej, aby skopiować zawartość w jakiś inny List<Integer>podobny

ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Premraj
źródło
Myślę, że oddzielne definiowanie funkcji jest nieeleganckie. Myślę, że można to również zrobić anonimowo i jednoetapowo
Marc,
jak wyodrębnić elementy będące ciągami znaków (float itp.) z listy?
wrzesień
22

Dzięki Premraj za alternatywną opcję fajną, przegłosowaną.

Użyłem apache CollectionUtils i BeanUtils. W związku z tym jestem zadowolony z działania następującego kodu:

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

Warto wspomnieć, że porównuję wydajność guawy ( dostarczonej przez Premraj ) z narzędziami z kolekcji, których użyłem powyżej i zdecyduję, która z nich jest szybsza.

Javatar
źródło
3
Wolę guawa od kolekcji apaczów, niezależnie od wydajności z powodów takich jak - bardziej nowoczesne, generyczne, nie używają odbić do transformacji itp. Ogólnie guawa jest znacznie bardziej nowoczesna niż kolekcje
apaszów
1
Tak, ale w rzeczywistości nie wydaje mi się dobrym pomysłem ograniczanie jego użycia przez obowiązkową potrzebę dodatkowej deklaracji wyliczenia dla każdego typu obiektu, który chcę przekształcić.
Javatar
Cóż, to zależy od twojego projektu - możesz mieć interfejs, Identifiablektóry mówi, który definiuje getId()metodę, a następnie możesz użyć tego pojedynczego wzorca pojedynczego wyliczenia, aby zwykle wyodrębnić Id.
Premraj
2
guawa jest zdecydowanie bardziej wydajna niż narzędzia Bean, ponieważ później wykorzystuje odbicia ..
ravi
2
Używanie nazwy właściwości jako String w konstruktorze new BeanToPropertyValueTransformer ("id") jest czymś, czego zdecydowanie staram się unikać. Refaktoryzacja będzie trudna! Ale szukam rozwiązania, aby to obejść. W międzyczasie zdecyduję się na rozwiązanie Guava, rozwlekłe, ale bezpieczne.
redochka
7

Rozwiązanie wyrażenia lambda Java 8:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
Opster Elasticsearch Pro-Vijay
źródło
Jeśli Java <1.8, użyj Apache CollectionUtils. Przykład: Collection <String> firstnames = CollectionUtils.collect (userlist, TransformerUtils.invokerTransformer ("getFirstname"));
a_subscriber
5

Jeśli ktoś dotrze tu po kilku latach:

List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
Jonathan Perrotta
źródło
I właśnie mnie uratowałeś :)
Behrad3d
-5

Niemożliwe jest to matematycznie bez pętli. Aby stworzyć odwzorowanie F dyskretnego zbioru wartości na inny dyskretny zbiór wartości, F musi działać na każdym elemencie ze zbioru źródłowego. (Zasadniczo wymagana jest do tego pętla).

Biorąc to pod uwagę:

Dlaczego potrzebujesz nowej listy? Możesz podejść do problemu, który rozwiązujesz w niewłaściwy sposób.

Jeśli masz listę Student, to podczas iteracji tej listy dzieli Cię tylko krok lub dwa od iteracji po numerach identyfikacyjnych uczniów.

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

Jeśli masz inny problem, skomentuj / zaktualizuj pytanie, a postaramy się Ci pomóc.

Zéychin
źródło
Na początku dziękuję za odpowiedź. Jednak to dramatyczne, że powiedziałeś, że potrzeba nowej listy jest niewłaściwa. Ponieważ zależy to od pojemności i zakresu potrzeb. Dlatego twoje podejście jest niewłaściwe :) Ponownie moja wzmianka o „możliwości osiągnięcia tego bez pętli” jest o wygodniejszym sposobie, aby kod nie stał się nieczytelny i mało wydajny. W każdym razie, już znalazłem sposób, aby to osiągnąć, trochę się nim podzielę.
Javatar
Nie powiedziałem, że to zły sposób, ale może to być zły sposób, w zależności od problemu, który próbujesz rozwiązać.
Zéychin