2010-07-07 1 views
7

Я хотел бы найти способ принять подпрограмму конкретного объекта ниже и аббревиатуру ее в метод, который вы можете передать классу, списку и имени поля, чтобы получить обратно Карта. Если бы я мог получить общий указатель на используемый шаблон или, и т. Д., Который мог бы начать меня в правильном направлении.Java: как преобразовать список <?> на карту <String,?>

Map<String,Role> mapped_roles = new HashMap<String,Role>(); 
    List<Role> p_roles = (List<Role>) c.list(); 
    for (Role el : p_roles) { 
     mapped_roles.put(el.getName(), el); 
    } 

к этому вопросу? (Псевдокод)

Map<String,?> MapMe(Class clz, Collection list, String methodName) 
    Map<String,?> map = new HashMap<String,?>(); 
    for (clz el : list) { 
     map.put(el.methodName(), el); 
    } 

Возможно ли это?

+0

@Aircule: Здесь можно использовать старый шутер-функтор. –

+0

Я не понимаю. Вы хотите иметь список вещей 'x' и преобразовать его в карту, где ключ - это имя объекта, а значение - вещь? – OscarRyz

+0

right, ключ - это строковое значение, возвращаемое переданным методом (объект Person имеет имя метода(), имя человека становится ключом), значение - это сам объект. Его гораздо проще сравнивать строки, чем иметь объект для сравнения. – ebt

ответ

1

Вот что я буду делать. Я не совсем уверен, если я обработки дженерики правильно, но да ладно:

public <T> Map<String, T> mapMe(Collection<T> list) { 
    Map<String, T> map = new HashMap<String, T>(); 
    for (T el : list) { 
     map.put(el.toString(), el); 
    } 
    return map; 
} 

Просто передать коллекцию ему, и ваши классы реализуют ToString(), чтобы вернуть имя. Полиморфизм позаботится об этом.

+0

Мне нравится идея использования этого против отражения, я должен реализовать оба метода , Спасибо – ebt

+2

Поскольку это работает только с 'toString()' как функция для получения ключа индекса, это не очень хорошее общее решение и, очевидно, не работает ни для какого произвольного свойства объекта. Решение отражения еще хуже и прерывается без ошибок компилятора при рефакторинге. – ColinD

+1

В этом случае вы можете использовать public static Карта . с BaseModel, содержащим, например, идентификатор поля и getId() ... Очевидно, что это будет работать только в том случае, если набор объектов, который вы пытаетесь преобразовать в карту, имеет тип BaseModel – Alexis

1

Использование отражения и дженерики:

public static <T> Map<String, T> MapMe(Class<T> clz, Collection<T> list, String methodName) 
throws Exception{ 
    Map<String, T> map = new HashMap<String, T>(); 
    Method method = clz.getMethod(methodName); 
    for (T el : list){ 
    map.put((String)method.invoke(el), el); 
    } 
    return map; 
} 

В документации, убедитесь, что вы говорите, что тип возвращаемого значения метода должен быть строкой. В противном случае он будет генерировать исключение ClassCastException, когда он попытается применить возвращаемое значение.

+0

+1 В такие моменты, как и я, мне нужен вариант «любимого ответа». –

+0

безупречный, это имеет смысл. Спасибо – ebt

+7

-1 - это может сработать в хороший день, но это хрупкий ... и, вероятно, намного дороже исходного кода. –

3

Избегать отражения, как чумы.

К сожалению, синтаксис Java для этого является подробным. (Недавнее предложение JDK7 бы сделать гораздо больше consise.)

interface ToString<T> { 
    String toString(T obj); 
} 

public static <T> Map<String,T> stringIndexOf(
    Iterable<T> things, 
    ToString<T> toString 
) { 
    Map<String,T> map = new HashMap<String,T>(); 
    for (T thing : things) { 
     map.put(toString.toString(thing), thing); 
    } 
    return map; 
} 

В настоящее время называют как:

Map<String,Thing> map = stringIndexOf(
    things, 
    new ToString<Thing>() { public String toString(Thing thing) { 
     return thing.getSomething(); 
    } 
); 

В JDK7, это может быть что-то вроде:

Map<String,Thing> map = stringIndexOf(
    things, 
    { thing -> thing.getSomething(); } 
); 

(возможно, потребуется a yield.)

+1

verbose и примерно такой же прозрачный, как mud :), что потребует Метод toString более прагматичен? – ebt

+1

@ebt Я не думаю, что что-то не так с ясностью. Названия колонок можно было бы лучше выбрать./Я не понимаю вашего комментария о методе 'toString'. Вы имеете в виду 'Object.toString' на' T' - это не очень полезно. –

+0

ОК, взял несколько итераций, чтобы собрать его вместе.Внедрение toString требует, чтобы ваши входные объекты реализовали toString, а не предполагали, что существует метод toString и выдает ошибку во время выполнения (хотя вы можете добавить броски по методу вправо?) – ebt

33

Использование Guava (formerly Google Collections):

Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, Functions.toStringFunction()); 

Или, если вы хотите поставить свой собственный метод, который делает String из объекта:

Map<String,Role> mappedRoles = Maps.uniqueIndex(yourList, new Function<Role,String>() { 
    public String apply(Role from) { 
    return from.getName(); // or something else 
    }}); 
+3

Я бы рекомендовал ссылку на Guava, а не на Google Collections, так как Guava официально вытеснил его. – ColinD

+0

Я не * думаю * вопрошающий хочет 'Object.toString'. Или, по крайней мере, вопрос, похоже, подразумевает, что он не хочет этого. –

+1

@Tom Затем он должен только поставить свою собственную функцию вместо сборной строки toString – Jorn

1

Если вы уверены, что каждый объект в List будет иметь уникальный индекс, использование Guava с предложением Йорна Maps.uniqueIndex.

Если, с другой стороны, более одного объекта могут иметь одно и то же значение для поля индекса (что, хотя это и не верно для вашего конкретного примера, возможно, верно во многих случаях использования для такого рода вещей), более общий способ это индексирование заключается в использовании Multimaps.index(Iterable<V> values, Function<? super V,K> keyFunction) для создания ImmutableListMultimap<K,V>, который сопоставляет каждую клавишу с одним или несколькими соответствующими значениями.

Вот пример, который использует пользовательский Function, который создает индекс для конкретного свойства объекта:

List<Foo> foos = ... 
ImmutableListMultimap<String, Foo> index = Multimaps.index(foos, 
    new Function<Foo, String>() { 
     public String apply(Foo input) { 
     return input.getBar(); 
     } 
    }); 

// iterate over all Foos that have "baz" as their Bar property 
for (Foo foo : index.get("baz")) { ... } 
3

Java 8 потоков и ссылка метод сделать это так просто не нужен вспомогательный метод для Это.

Map<String, Foo> map = listOfFoos.stream() 
    .collect(Collectors.toMap(Foo::getName, Function.identity())); 

Если там могут быть дубликаты ключей, вы можете объединить значения с toMap overload that takes a value merge function, или вы можете использовать groupingBy собрать в список:

//taken right from the Collectors javadoc 
Map<Department, List<Employee>> byDept = employees.stream() 
    .collect(Collectors.groupingBy(Employee::getDepartment)); 

Как было показано выше, ничто из этого не является специфичным to String - вы можете создать индекс для любого типа.

Если у вас есть много объектов для обработки и/или ваша функция индексации дорого, вы можете идти параллельно с помощью Collection.parallelStream() или stream().parallel() (they do the same thing). В этом случае вы можете использовать toConcurrentMap или groupingByConcurrent, так как они позволяют реализации потока просто вставлять элементы в ConcurrentMap вместо создания отдельных карт для каждого потока, а затем их слияния.

Если вы не хотите комментировать Foo::getName (или какой-либо конкретный метод) на сайте вызова, вы можете использовать функцию, переданную вызывающим абонентом, хранящуюся в поле и т. Д. Тот, кто на самом деле создает функцию, может все еще используют ссылку на метод или лямбда-синтаксис.

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