Ответ 1
Если вы используете Java 8, вы можете воспользоваться Stream API:
List<String> list = objects.stream()
.map(CustomObject::getActualText)
.collect(Collectors.toList());
Мне было интересно, как выглядит такой код:
List<String> list = new ArrayList<String>();
for(CustomObject co : objects) {
list.add(co.getActualText());
}
Можно ли написать иначе? Я имею в виду, конечно, в какой-то момент будет цикл, но мне интересно, есть ли использование API, которое я игнорирую
Если вы используете Java 8, вы можете воспользоваться Stream API:
List<String> list = objects.stream()
.map(CustomObject::getActualText)
.collect(Collectors.toList());
Если у вас есть 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();
}
}
Конечно, Apache Commons и Guava также предоставляют способы избежать циклов без использования 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();
}
}
);
Хотя это явно смехотворное предложение: вы можете избежать циклов, добавив их рекурсивно.
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);
}
}
Если вы используете Eclipse Collections (ранее Коллекции GS), вы можете написать следующее в Java 8:
MutableList<CustomObject> objects = ...
MutableList<String> result = objects.collect(CustomObject::getActualText);
С помощью Java 5-7 вы можете использовать анонимный внутренний класс, представляющий тип SAM Function с помощью метода collect.
MutableList<CustomObject> objects = ...
MutableList<String> result = objects.collect(new Function<CustomObject, String>() {
public String valueOf(CustomObject object){
return object.getActualText();
}
});
Примечание: Я являюсь коммиттером для коллекций Eclipse
Использование потоков будет более идиоматичным в Java 8, но если вам нравится, чтобы он был ближе к обычным подходам на основе цикла, вы можете использовать forEach
:
objects.forEach(co -> list.add(co.getActualText()) );
Для достижения действительно хорошей эффективности при копировании диапазона данных между двумя типами списков, которые неизвестны друг другу, должен существовать механизм, с помощью которого "доверенный" тип может запрашивать у каждого из них, чтобы связать связанные с ним массивы поддержки с рядом элементов, а затем использовать операцию массовой копии для перемещения данных от одного к другому. Можно было бы полностью написать такой класс в Java, используя метод GetArraySource
передать конструктору доверенного класса ArraySource
объект, который он мог бы использовать для запроса массива поддержки, связанного с определенным элементом (возврат был бы включают в себя базовый массив и диапазон элементов, включенных в него). Код, требующий копию, вызовет GetArraySource
и передаст ArraySource
, возвращенный тем самым в метод назначения CopyFromArraySource
, который затем может запросить ArraySource
скопировать один или несколько диапазонов элементов в свой собственный массив поддержки (s).
Если ArraySource
был классом, поставляемым с Java, и Oracle точно документировал, что он будет делать с полученными массивами, тогда типы, такие как ArrayList
и String
, могли бы показать их содержимое как ArraySource
или принимать внешние данные из ArraySource
, не подвергая их массив любому коду, который может его нарушить.
К сожалению, если Oracle не включит такую вещь в дистрибутив Java, поддержка, вероятно, будет слишком скудной, чтобы быть полезной. Нехорошо, чтобы исходный список поддерживал один такой класс, назначение - другое, а код, требующий операции копирования, - третий. Все три класса должны поддерживать один и тот же класс доверенных массивов-сегментов-копий-помощников.