Java:如何在<T>不迭代的情况下从List转换为Map<f1(T),List(f2(T))>



我有一个对象列表,我需要将其转换为一个映射,其中键是每个元素的函数,值是每个元素的另一个函数的列表。这实际上是根据元素的一个函数对元素进行分组。

例如,假设有一个简单的元素类:
class Element {
    int f1() { ... }
    String f2() { ... }
}

和一个列表:

[
    { f1=100, f2="Alice" },
    { f1=200, f2="Bob" },
    { f1=100, f2="Charles" },
    { f1=300, f2="Dave" }
]

那么我想要一张如下的地图:

{
    {key=100, value=[ "Alice", "Charles" ]},
    {key=200, value=[ "Bob" ]},
    {key=300, value=[ "Dave" ]}
}
谁能建议一种简洁的方法在Java 中做到这一点而不迭代?LambdaJ的group方法与Guava的Maps.transform方法的组合几乎可以实现,但group不能生成地图。

番石榴有地图。uniqueIndex(可迭代值,函数keyFunction)和Multimaps。index(Iterable values, Function keyFunction),但它们不转换值。有一些请求添加实用程序方法来做你想做的事情,但现在,你必须自己使用Multimaps.index()和Multimaps.transformValues():

static class Person {
    private final Integer age;
    private final String name;
    public Person(Integer age, String name) {
        this.age = age;
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public String getName() {
        return name;
    }
}
private enum GetAgeFunction implements Function<Person, Integer> {
    INSTANCE;
    @Override
    public Integer apply(Person person) {
        return person.getAge();
    }
}
private enum GetNameFunction implements Function<Person, String> {
    INSTANCE;
    @Override
    public String apply(Person person) {
        return person.getName();
    }
}
public void example() {
    List<Person> persons = ImmutableList.of(
            new Person(100, "Alice"),
            new Person(200, "Bob"),
            new Person(100, "Charles"),
            new Person(300, "Dave")
    );
    ListMultimap<Integer, String> ageToNames = getAgeToNamesMultimap(persons);
    System.out.println(ageToNames);
    // prints {100=[Alice, Charles], 200=[Bob], 300=[Dave]}
}
private ListMultimap<Integer, String> getAgeToNamesMultimap(List<Person> persons) {
    ImmutableListMultimap<Integer, Person> ageToPersons = Multimaps.index(persons, GetAgeFunction.INSTANCE);
    ListMultimap<Integer, String> ageToNames = Multimaps.transformValues(ageToPersons, GetNameFunction.INSTANCE);
    // Multimaps.transformValues() returns a *lazily* transformed view of "ageToPersons"
    // If we want to iterate multiple times over it, it's better to create a copy
    return ImmutableListMultimap.copyOf(ageToNames);
}

一个可重用的实用方法可以是:

public static <E, K, V> ImmutableListMultimap<K, V> keyToValuesMultimap(Iterable<E> elements, Function<E, K> keyFunction, Function<E, V> valueFunction) {
    ImmutableListMultimap<K, E> keysToElements = Multimaps.index(elements, keyFunction);
    ListMultimap<K, V> keysToValuesLazy = Multimaps.transformValues(keysToElements, valueFunction);
    return ImmutableListMultimap.copyOf(keysToValuesLazy);
}

我想我们可以通过使用Function<? extends E, K>或其他东西来改进签名中的泛型,但是我没有时间进一步研究…

现在使用Java8,您可以这样做:

static class Element {
    final int f1;
    final String f2;
    Element(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    int f1() { return f1;}
    String f2() { return f2; }
}
public static void main(String[] args) {
    List<Element> elements = new ArrayList<>();
    elements.add(new Element(100, "Alice"));
    elements.add(new Element(200, "Bob"));
    elements.add(new Element(100, "Charles"));
    elements.add(new Element(300, "Dave"));
    elements.stream()
            .collect(Collectors.groupingBy(
                    Element::f1,
                    Collectors.mapping(Element::f2, Collectors.toList())
                    ))
            .forEach((f1, f2) -> System.out.println("{"+f1.toString() + ", value="+f2+"}"));
}

在Apache的CollectionUtils中添加一个API以将列表转换为Map,但我没有看到不使用foreach结构的任何理由,您是否面临任何问题?Transform会做同样的事情,你可以很容易地通过foreach,循环无法避免。

编辑:

这里是Apache论坛讨论的链接http://apache-commons.680414.n4.nabble.com/Convert-List-to-Map-td747218.html

我不知道你为什么不想迭代。JDK不支持转换,但是你可以自己实现。

如果你担心性能,即使JDK支持它,它也会迭代它。

相关内容

  • 没有找到相关文章

最新更新