Скажем, у меня есть боб как ниже.
class Customer{
private String code;
private String name;
private Integer value;
//getters setters omitted for brevity
}
Затем из метода я получаю a List<Customer>
назад. Теперь скажем, я хочу получить список всего участника "имя" из Списка. Очевидно, я могу пересечь и создать a List<String>
из элемента "называют" меня.
Однако я задавался вопросом, существует ли короткий путь или больше effiecient пути к этой технике, которую любой знает. Например, если я хочу добраться, список всех ключей в Карте возражают, что я добираюсь, делают map.keySet (). Что-то вдоль той строки - то, что я пытаюсь узнать.
В Guava есть Lists.transform
который может преобразовать List
в List
, используя предоставленную Function
(или, скорее, Function super F,? extends T>
).
Из документации:
public static
List transform( List fromList, Function super F,? extends T> function ) Возвращает список, который применяет
функцию
к каждому элементуfromList
. Возвращаемый список является преобразованным представлениемfromList
; изменения вfromList
будут отражены в возвращаемом списке и наоборот.Функция
function
применяется лениво, вызываясь по мере необходимости.
Подобные преобразования в реальном времени также предоставляются следующим образом:
Iterables.transform
(Iterable
to Iterable
)Iterators. transform
(Iterator
to Iterator
)Collections2.transform
(Collection
to Collection
)Maps. transformValues
(Map
to Map
)Похоже, вы ищете Java-эквивалент map
-функции Perl. Подобные вещи могут быть добавлены в библиотеку коллекций, как только (если) Java получит замыкания. До тех пор, я думаю, это лучшее, что вы можете сделать:
List<String> list = new ArrayList<String>(customers.size());
for ( Customer c : customers ) {
list.add(c.getName());
}
Вы также можете написать функцию map
, которая использует простой интерфейс для предоставления функции отображения. Что-то вроде этого:
public interface Transform<I, O> {
O transform(I in);
}
public <I, O> List<O> map(Collection<I> coll, Transform<? super I, ? extends O> xfrm) {
List<O> list = new ArrayList<O>(coll.size());
for ( I in : coll ) {
list.add(xfrm.transform(in));
}
return list;
}
Я думаю, что это то, что вы должны закодировать сами, в цикле.
Вы можете использовать интерфейс Converter LambdaJ и иметь следующую строку:
List<String> customerNames = convert(customerList, new Converter<Customer,String>() {
public String convert(Customer customer) {
return customer.getName();
}
});
Вам нужно использовать цикл, но искомая функция на функциональных языках называется map
. В Java можно реализовать map
, хотя это, как правило, довольно неэлегантно; вот версия, которую я реализовал много лет назад в моей библиотеке «вещи, которые должны быть в Java, но по какой-то причине нет»:
public interface MapFunction<T, U> {
public U map(T source);
}
public static <T, U> U[] map(T[] objects, MapFunction<T, U> f) {
if(objects.length == 0) {throw new IllegalArgumentException("Can't map onto an empty array");}
@SuppressWarnings("unchecked") U[] rtn = (U[])Array.newInstance(f.map(objects[0]).getClass(), objects.length);
for(int i = 0; i < objects.length; i++)
rtn[i] = f.map(objects[i]);
return rtn;
}
Используя это, вы могли бы сделать:
List<Customer> list = yourFunction();
List<String> names = Arrays.asList(map(list.toArray(new Customer[0]), new MapFunction<Customer, String>() {
public String map(Customer c) {
return c.getName();
}
}));
Естественно, вы могли бы изменить карту, чтобы она принимала коллекции вместо массивов, которые устранит необходимость в Arrays.asList
и List.toArray
можно использовать что-то вроде этого: http://code.google.com/p/lambdaj/
Используя Guava , вы можете использовать функцию вместе с Iterables.transform , Collections2.transform ] или Lists.transform для создания Iterable
, Collection
или List
соответственно.
Iterable<String> names = Iterables.transform(customers,
new Function<Customer, String>() {
public String apply(Customer from) {
return from.getName();
}
});
Возвращенный Iterable
является ленивым и применяет функцию к базовому списку, когда вы выполняете итерацию по нему. Для списка List
, содержащего имена, вы можете использовать:
List<String> names = Lists.transform(...);
или
ImmutableList<String> names = ImmutableList.copyOf(Iterables.transform(...));
Конечно, записывая реализацию анонимного внутреннего класса Function
каждый раз, когда вы хотите сделать это некрасиво и многословно, поэтому вы можете сделать Function
константой, доступной из класса Customer
, например, с именем Customer.NAME
.
Тогда преобразование выглядит намного лучше (особенно со статическим импортом):
for (String name : transform(customers, Customer.NAME)) { ... }
Я также писал об использовании интерфейсов для определенных свойств объектов (таких как name
здесь), чтобы помочь с консолидацией таких функций на моем блог здесь .
.... есть более короткий путь или более эффективный способ
Итак, вы ищете более эффективный способ сделать это:
List<String> names = new ArrayList<String>();
for( Customer customer : yourCustomerList ) {
names.add( customer.getName() );
}
? !!!!
Или просто другой способ?
Все предыдущие ответы на самом деле не более эффективны с точки зрения времени выполнения или кодирования. Однако они, без сомнения, более гибкие.
Другой альтернативой может быть включение Scala Groovy в ваш код Java и использование этого:
list.map (_.name)
list.collect { it.name }
Если скомпилирован, классы Groovy могут использоваться из Java, или вы можете подключить их как скрипт.
Вот пример для данного класса Customer
, использующего Groovy в качестве скрипта.
List<Customer> customers = Arrays.asList( new Customer[]{
new Customer("A","123",1),
new Customer("B","456",2),
new Customer("C","789",3),
new Customer("D","012",4)
});
setVariable(customers, "list");
evaluate("names = list.collect { it.name } ");
List<String> names = (List<String>) getVariable("names");
System.out.println("names = " + names);
Вывод:
names = [A, B, C, D]
примечание: я извлек метод для удобства чтения, но вы можете увидеть его ниже
Но, опять же, это просто другое, не более эффективное, чем обычный цикл for.
Вот полный исходный код . Для его запуска вам просто нужны Java1.6 и Groovy в пути к классам.