2015-03-19 4 views
28

мне было интересно, код вроде этого:Есть ли способ избежать циклов при добавлении в список?

List<String> list = new ArrayList<String>(); 
for(CustomObject co : objects) { 
    list.add(co.getActualText()); 
} 

Можно ли писать по-другому? Я имею в виду, конечно, в какой-то момент будет петля, но мне интересно, если есть использование API я игнорирую

+1

это называется Clojure; D Вы можете прочитать о (conj) здесь: http://clojuredocs.org/clojure.core/conj – sova

+2

его также называют Scala, Haskell и ... (вставьте название любого мощного языка программирования) –

ответ

26

Если вы используете Java 8, вы можете воспользоваться поток API:

List<String> list = objects.stream() 
          .map(CustomObject::getActualText) 
          .collect(Collectors.toList()); 
+15

TBH IMHO это фактически нечитаемо для простой задачи. Речь идет не о вашем ответе, а о самом подходе Java. – Jim

+9

Не совсем соглашаясь с Джим, это также вопрос привыкания к новой концепции. Хотя в Scala было бы красивее с простым val list = objects.map (_. GetActualText) –

+7

У людей .NET есть LINQ уже много лет, и ясно, что это гораздо лучший подход, чем циклы построения списков почти все случаи. Я думаю, что людям Java просто нужно обойтись, чтобы изменить менталитет. Этот код не является нечитаемым сам по себе. Это неопровержимо для неопытных. Это временное состояние. – usr

20

Если у вас есть Java 8, что о:

objects.forEach(item -> list.add(item.getActualText())); 

Внутренне еще цикл, хотя.

РЕДАКТИРОВАТЬ немного Офф-тей: ИМО Это наиболее читаемое и лучшее решение. Почему бы просто не использовать foreach, о котором вы могли бы спросить. Ответ: так как коллекция выбирает лучший способ перебора элементов. Например, ArrayList не использует итератор, потому что он знает лучше, чем вы:

@Override 
public void forEach(Consumer<? super E> action) { 
    Objects.requireNonNull(action); 
    final int expectedModCount = modCount; 
    @SuppressWarnings("unchecked") 
    final E[] elementData = (E[]) this.elementData; 
    final int size = this.size; 
    for (int i=0; modCount == expectedModCount && i < size; i++) { 
     action.accept(elementData[i]); 
    } 
    if (modCount != expectedModCount) { 
     throw new ConcurrentModificationException(); 
    } 
} 
+0

Я хочу добавить, что я с тех пор вырос до «функционального» подхода, показанного Константином Йовковым. Стиль кажется очень чистым, даже если он выглядит ужасно и может плохо работать в java. – Felk

3

Использования потоков будет более идиоматическими в Java 8, но если вы хотите, чтобы быть ближе к традиционному подходу, основанной петли вы можете использовать forEach:

objects.forEach(co -> list.add(co.getActualText())); 
6

Хотя очевидно, немного смешное предложение: вы можете избежать петли, добавив их рекурсивно.

void add(List<? super String> receiver, CustomObject[] objects) { 
    addRec(receiver, toAdd, 0, objects.length()); 
} 

void addRec(List<? super String> receiver, CustomObject[] objects, int start, int end) { 
    if (start + 1 == end) { 
    receiver.add(objects[start].getActualText()); 
    } else if (start != end) { 
    int mid = (start + end)/2; 
    addRec(receiver, objects, start, mid); 
    addRec(receiver, objects, mid, end); 
    } 
} 
+0

Поскольку операции O (n) действительно требуют динамически изменяющегося потока управления (т. Е. Цикла), это будет моим ответом. Это немного троллий на императивном языке, но на чистом функциональном языке это все, что у вас есть. +1. – imallett

9

Конечно, Apache Commons и гуавы также обеспечивают способы избежать петли без использования Java 8.

Commons CollectionUtils.collect:

CollectionUtils.collect(objects, Transformer.invokerTransformer("getActualText"), list); 

Guava Lists.transform:

List<String> list = Lists.transform(objects, 
    new Function<CustomObject, String>() { 
     public String apply(CustomObject co) { 
      return co.getActualText(); 
     } 
    } 
); 
+2

В Java 8 пример Guava можно записать гораздо более кратко, заменив анонимный класс на лямбда: 'Lists.transform (objects, (co) -> co.getActualText())' – meriton

4

Если вы используете Eclipse Collections (ранее GS Collections) вы можете написать в Java 8:

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(CustomObject::getActualText); 

С Java 5 - 7 вы можете использовать анонимный внутренний класс, представляющий тип SAM Function с сборным методом.

MutableList<CustomObject> objects = ... 
MutableList<String> result = objects.collect(new Function<CustomObject, String>() { 
    public String valueOf(CustomObject object){ 
     return object.getActualText(); 
    } 
}); 

Примечание: Я коммиттер для Eclipse, Коллекции

0

Для достижения действительно хорошую эффективность при копировании диапазона данных между двумя типами списков, которые неизвестны друг другу, должен быть механизм с помощью которого «доверенный» тип может попросить каждого разоблачить массив (ы) поддержки, связанный с рядом элементов, а затем использовать операцию массовой копии для перемещения данных от одного к другому. Можно было бы полностью написать такой класс в Java, получив способ GetArraySource передать конструктору доверенного класса ArraySource объект, который он мог бы использовать для запроса массива поддержки, связанного с конкретным элементом (возврат будет включать в себя массив поддержки и диапазон элементов, включенных в него).Код, требующий копии, вызовет GetArraySource и передаст ArraySource, возвращенный тем самым методу CopyFromArraySource списка адресата, который затем может запросить ArraySource для копирования одного или нескольких диапазонов элементов в собственный массив (ы) поддержки.

Если ArraySource был класс поставляется с Java и Oracle документально, что именно он будет делать с массивами, которые были получены, то можно было бы для типов, таких как ArrayList и String, чтобы выставить их содержимое как ArraySource или принимать внешние данные от ArraySource, без неправильного отображения их массива на любой код, который может его нарушить.

К сожалению, если Oracle не включит такую ​​вещь в дистрибутив Java, поддержка, вероятно, будет слишком скудной, чтобы быть полезной. Нехорошо, чтобы исходный список поддерживал один такой класс, назначение - другое, а код, требующий операции копирования, - третий. Все три класса должны поддерживать один и тот же класс доверенных массивов-сегментов-копий-помощников.

+0

Я думаю, что у вас что-то похожее на C memcopy в виду фактических данных. Правильно? – Jim

+0

@ Jim: Я ожидал бы, что операции массового копирования в 'Arrays', вероятно, будут реализованы с использованием чего-то примерно эквивалентного' memcpy', как только он определит, что в исходном массиве не может существовать какой-либо объект типа массива адресата удерживать [попытку, например, выполнить массовые экземпляры копий из 'Cat []' в 'Animal []' могут просто копировать ссылки напрямую, но, наоборот, необходимо будет проверить тип каждого ссылочного объекта перед копированием ссылки]. – supercat

Смежные вопросы