2010-07-07 17 views
7

Ich möchte einen Weg finden, um die objektspezifische Routine unten zu nehmen und es zu einer Methode zu abstrahieren, die eine Klasse, eine Liste und einen Feldnamen zurückgeben kann Karte. Wenn ich einen allgemeinen Zeiger auf das verwendete Muster bekommen könnte oder etc, könnte mich das in die richtige Richtung bringen.Java: Wie konvertiert man eine Liste <?> in eine Map <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); 
    } 

zu diesem? (Pseudocode)

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

ist es möglich?

+0

@Aircule: Man könnte den alten Funktor-Hack hier gut gebrauchen. –

+0

Ich verstehe es nicht. Du willst eine Liste von 'x' Dingen haben und sie in eine Karte umwandeln, wo der Schlüssel der Dingname ist und der Wert das Ding ist? – OscarRyz

+0

Richtig, der Schlüssel ist ein Zeichenfolgenwert, der von der übergebenen Methode zurückgegeben wird (Objekt Person hat Methodenname(), Personenname wird der Schlüssel), der Wert ist das Objekt selbst. Es ist viel einfacher Strings zu vergleichen, als ein Objekt zum Vergleich zur Hand zu haben. – ebt

Antwort

1

Hier ist, was ich tun würde. Ich bin nicht ganz sicher, ob ich Generika am richtigen Umgang, aber na ja:

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; 
} 

passieren nur eine Sammlung, um es, und lassen Sie Ihre Klassen implementieren toString() den Namen zurückzukehren. Polymorphismus wird dafür sorgen.

+0

Ich mag die Idee der Verwendung dieser vs Reflexion, muss ich beide Methoden implementieren . Danke – ebt

+2

Da dies nur mit 'toString()' als Funktion zum Abrufen des Indexschlüssels funktioniert, ist es keine sehr gute allgemeine Lösung und funktioniert offensichtlich nicht für irgendeine beliebige Eigenschaft eines Objekts. Die Reflexionslösung ist noch schlechter und bricht ohne Compilerfehler beim Refactoring. – ColinD

+1

Nun in diesem Fall können Sie public static verwenden Karte . mit BaseModel, das zum Beispiel die Feld-ID und getId() enthält ... Offensichtlich funktioniert das nur, wenn der Objektsatz, den Sie in die Karte konvertieren wollen, vom Typ BaseModel – Alexis

1

Mit Reflexion und Generika:

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; 
} 

In Ihrer Dokumentation, stellen Sie sicher, dass Sie erwähnen, dass der Rückgabetyp des Verfahrens wird ein String sein muss. Andernfalls wird eine ClassCastException ausgelöst, wenn versucht wird, den Rückgabewert zu übergeben.

+0

+1 Zu Zeiten wie diesen wünschte ich, es gäbe eine "Lieblingsantwort" -Option. –

+0

perfekt, das macht Sinn. Danke – ebt

+7

-1 - das könnte an einem guten Tag funktionieren, aber es ist zerbrechlich ... und wahrscheinlich viel teurer als der ursprüngliche Code. –

3

Vermeiden Sie Reflexionen wie die Pest.

Leider ist Java-Syntax dafür ausführlich. (Ein aktueller JDK7 Vorschlag würde es viel mehr consise machen.)

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; 
} 

Derzeit als nennen:

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

In JDK7, es so etwas wie sein:

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

(Möglicherweise müssen a yield da drin.)

+1

ausführlich und ungefähr so ​​klar wie Schlamm ist :), würde a erfordern toString-Methode ist pragmatischer? – ebt

+1

@ebt Ich glaube nicht, dass mit der Klarheit etwas nicht stimmt. Nun, Namen könnten besser gewählt sein. Ich verstehe deinen Kommentar zu einer 'toString'-Methode nicht. Meinst du "Object.toString" auf "T" - das wäre nicht sehr nützlich. –

+0

ok, nahm mich mehrere Iterationen, um es zusammenzufügen.Das Implementieren von toString erfordert, dass Ihre Eingabeobjekte toString implementieren, anstatt davon auszugehen, dass die Methode toString existiert und zur Laufzeit einen Fehler auslöst (obwohl Sie Würfe für die Methode hinzufügen könnten?) – ebt

33

Mit Guava (formerly Google Collections):

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

Oder, wenn Sie möchten, dass Ihre eigene Methode liefern, die ein String aus dem Objekt macht:

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

Ich würde empfehlen, Guava anstatt Google Collections zu verlinken, da Guava es offiziell abgelöst hat. – ColinD

+0

Ich denke nicht * der Fragesteller will 'Object.toString'. Oder zumindest scheint die Frage zu implizieren, es nicht zu wollen. –

+1

@Tom Dann muss er nur seine eigene Funktion anstelle der vorgefertigten toString one – Jorn

1

Wenn Sie sicher sind, dass jedes Objekt in den List einen eindeutigen Index haben wird, verwenden Guava mit Jorns Vorschlag von Maps.uniqueIndex.

Wenn andererseits mehr als ein Objekt den gleichen Wert für das Indexfeld haben kann (was zwar in vielen Anwendungsfällen für diese Art der Sache nicht zutrifft, aber nicht wahr ist), dann wird der Allgemeinerer Art und Weise dieser Indizierung ist es, Multimaps.index(Iterable<V> values, Function<? super V,K> keyFunction) zu verwenden, um eine ImmutableListMultimap<K,V> zu erstellen, die jeden Schlüssel einem oder mehreren übereinstimmenden Werten zuordnet.

Hier ist ein Beispiel, das eine benutzerdefinierte verwendet Function, die einen Index für eine bestimmte Eigenschaft eines Objekts erzeugt:

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-Streams und Verfahren Referenzen machen diese so einfach Sie für eine Hilfsmethode nicht brauchen es.

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

Wenn es doppelte Schlüssel sein kann, können Sie die Werte mit dem toMap overload that takes a value merge function aggregieren können, oder Sie können groupingBy verwenden, um in einer Liste zu sammeln:

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

Wie oben gezeigt, nichts davon ist spezifisch to String - Sie können einen Index für jeden Typ erstellen.

Wenn Sie eine Menge von Objekten zu verarbeiten und/oder Ihre Indizierungsfunktion ist teuer, können Sie parallel gehen von Collection.parallelStream() oder stream().parallel() (they do the same thing) verwendet wird. In diesem Fall könnten Sie toConcurrentMap oder groupingByConcurrent verwenden, da sie es der Stream-Implementierung ermöglichen, Elemente einfach in eine ConcurrentMap zu laden, anstatt für jeden Thread separate Maps zu erstellen und sie dann zusammenzuführen.

Wenn Sie nicht möchten, Foo::getName (oder eine bestimmte Methode) an der Aufruf-Site zu committen, können Sie eine Funktion von einem Aufrufer übergeben, in einem Feld gespeichert, etc .. Wer auch immer die Funktion erstellt kann Nutzen Sie weiterhin die Methodenreferenz oder die Lambda-Syntax.

Verwandte Themen