Как составить новый список со свойством объекта, находящегося в другом списке

81

Представьте, что у меня есть список определенных объектов:

List<Student>

И мне нужно создать еще один список, включая idsиз Studentsприведенного выше списка:

List<Integer>

Можно ли этого добиться, избегая использования цикла, используя коллекции apache или guava ?

Какие методы должны быть полезны в моем случае?

Джаватар
источник
Привет, я только что нашел это: stackoverflow.com/questions/737244/…
Javatar
Ответ Джона довольно крут, но если вы посмотрите на него, он также использует цикл. Любое любое решение будет использовать цикл - хотя он может быть вам не виден, но внутри он будет
hage
кто-то должен применить цикл, чтобы это сделать. либо вы, либо какая-то библиотека, которую вы можете использовать.
Sudmong
На самом деле ответ Джона не соответствует моей ситуации, поскольку я не хочу использовать цикл for. Я думаю, что где-то есть более удобный способ, но жду, пока я его найду :)
Джаватар,

Ответы:

175

Java 8 способ сделать это: -

List<Integer> idList = students.stream().map(Student::getId).collect(Collectors.toList());
Кошик
источник
2
Что, если мне нужен список кортежей? Что-то вроде [(Id, name), (Id, name)].
Coder95
Не все функции версии 1.8 поддерживаются для всех API Android. В частности, java.util.stream не поддерживается до API 24.
The Black Horse,
41

С Guava вы можете использовать такие функции, как -

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

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

и вы можете использовать эту функцию для преобразования списка студентов в идентификаторы, например -

Lists.transform(studentList, StudentToId.INSTANCE);

Конечно, он будет зацикливаться, чтобы извлечь все идентификаторы, но помните, что методы guava возвращают представление, а функция будет применяться только при попытке перебора. List<Integer>
Если вы не выполняете итерацию, цикл никогда не будет применяться.

Примечание: помните, что это представление, и если вы хотите повторить несколько раз, будет лучше скопировать содержимое в какой-то другой, List<Integer>например

ImmutableList.copyOf(Iterables.transform(students, StudentToId.INSTANCE));
Премрадж
источник
Я считаю неэлегантным определять функцию отдельно. Я думаю, что это также можно сделать анонимно и в одноэтапный процесс
Марк
как вы извлекаете строковые (плавающие и т. д.) члены списка?
sepehr
22

Благодаря Премраджу за альтернативный крутой вариант, проголосовали за.

Я использовал apache CollectionUtils и BeanUtils. Соответственно, меня устраивает выполнение следующего кода:

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

Стоит упомянуть, что я сравню производительность guava ( предоставляется Premraj ) и collectionUtils, которые я использовал выше, и выберу более быстрый.

Джаватар
источник
3
Я предпочитаю Guava коллекциям apache независимо от производительности по таким причинам, как - более современный, универсальный, не использует отражение для преобразований и т. Д. В целом guava намного современнее, чем коллекции apache - подробнее здесь
Premraj
1
Да, но на самом деле мне не кажется хорошей идеей ограничивать его использование обязательной необходимостью дополнительного объявления enum для каждого типа объекта, который я хочу преобразовать.
Javatar
Ну, это зависит от вашего дизайна - вы можете указать интерфейс, Identifiableкоторый определяет getId()метод, а затем вы можете использовать этот одиночный шаблон перечисления для общего извлечения идентификатора.
Premraj
2
Гуава определенно более эффективна, чем утилит Bean, так как в более поздних версиях используются отражения ..
ravi
2
Использование имени свойства в виде String в конструкторе new BeanToPropertyValueTransformer ("id") - это то, чего я определенно стараюсь избегать. Рефакторинг будет сложным! Но я ищу решение, чтобы обойти это. Тем временем я выберу решение Guava, подробное, но безопасное.
редочка
7

Решение лямбда-выражения Java 8:

List<Integer> iDList = students.stream().map((student) -> student.getId()).collect(Collectors.toList());
Opster Elasticsearch Pro-Vijay
источник
Если Java <1.8, используйте Apache CollectionUtils. Пример: Коллекция <String> firstnames = CollectionUtils.collect (список пользователей, TransformerUtils.invokerTransformer ("getFirstname"));
a_subscriber
5

Если кто-то попадет сюда через несколько лет:

List<String> stringProperty = (List<String>) CollectionUtils.collect(listOfBeans, TransformerUtils.invokerTransformer("getProperty"));
Джонатан Перротта
источник
И ты меня только что спас :)
Behrad3d
-5

Математически это невозможно сделать без цикла. Чтобы создать отображение F дискретного набора значений в другой дискретный набор значений, F должен работать с каждым элементом в исходном наборе. (Для этого в основном требуется цикл.)

Что, как говорится:

Зачем вам новый список? Вы можете неправильно подходить к решению любой проблемы.

Если у вас есть список Student, то при итерации по этому списку вам останется всего один-два шага от перебора идентификационных номеров учеников.

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

Если у вас возникла проблема другого рода, прокомментируйте / обновите вопрос, и мы постараемся вам помочь.

Зейчин
источник
Сначала спасибо за ответ. Однако драматично, что вы сказали, что необходимость в новом списке неправильная. Потому что это зависит от емкости и диапазона потребности. Следовательно, ваш подход неправильный :) И снова мое упоминание о «способности добиться этого без цикла» - это более удобный способ не сделать код нечитабельным и низкопроизводительным. В любом случае, я уже нашел способ добиться этого, собираюсь немного поделиться.
Javatar
Я не говорил , что это неправильный путь, но это может быть неправильно, в зависимости от проблемы , которую вы пытаетесь решить.
Zéychin