什么是在地图地图中打破Java列表的更好方法?

时间:2016-05-19 14:57:22

标签: java java-stream

我们有

List<persons> persons; 

我们需要

 Map<age,Map<income,Person>> results

我现在的做法是:

      ages.stream().forEach(a -> {
        Map<Integer,Person> tmpMap  = new HashMap<>();
        incomes.stream().forEach(i -> {
            Person p = persons.stream().filter(
            u -> u.getAge() == a.getAge() && 
             u.getIncome() == i.getIncome())
             .findAny().orElse(null);
             tmpMap.put(i.getIncome(), p);
        });
       returns.put(a.getAge(),tmpMap);
    });
似乎应该有更好的方法来做到这一点。

6 个答案:

答案 0 :(得分:3)

这应该可以解决问题

List<person> persons = Arrays.asList(
    new person(22, 1000), 
    new person(25, 1500), 
    new person(22, 2500), 
    new person(32, 5000)
);
Map<Integer, Map<Integer, List<person>>> map = persons.stream().collect(
    groupingBy(person::getAge, groupingBy(person::getIncome))
);
System.out.println(map);

<强>输出:

{32={5000=[person{age=32, income=5000}]}, 22={2500=[person{age=22, income=2500}], 1000=[person{age=22, income=1000}]}, 25={1500=[person{age=25, income=1500}]}}

NB:结果并不完全符合您的预期,因为您会获得Map<Integer, Map<Integer, List<person>>>而不是Map<Integer, Map<Integer, person>>,但我认为您的初始问题不正确,因为如果您有两个年龄和收入相同的人,你的地图中只有一个人而不是两个人

答案 1 :(得分:3)

这看起来很有效。

List<Person> people = Arrays.asList(
        new Person("One", 21, 100),
        new Person("Two", 21, 75),
        new Person("Three", 42, 100),
        new Person("Four", 42, 120),
        new Person("Five", 9, 100)
);
Map<Integer, Map<Integer, Person>> map = people.stream()
        // Gather all ages into a Map<Age,List<Person>>
        .collect(Collectors.groupingBy(Person::getAge))
        // Walk that transient Map.
        .entrySet().stream()
        .collect(Collectors.toMap(
                // Key is the age.
                Map.Entry::getKey,
                // Value is a Map<income,person>
                e -> e.getValue()
                // Roll each of the same age into a Map<Income,Person>
                .stream().collect(
                        Collectors.toMap(
                                // Key is income.
                                Person::getIncome,
                                // Value is the Person.
                                Function.identity()
                        ))));

我使用Map<Age,List<Person>>将您的列表滚动到groupingBy,然后流式传输它的entrySet并将其收集到最终表单中。

如果两个年龄相同的人有相同的收入,这将失败,因为这会违反内部地图。如果您对生成Map<Integer, Map<Integer, List<Person>>>的自然增强感到满意,请使用Alexander's suggestion

<强>加

@Holger在评论中指出,这可以用更简单,更优雅的方式完成。请改用/

使用此表格
Map<Integer, Map<Integer, Person>> map2 = people.stream()
        .collect(
                Collectors.groupingBy(
                        Person::getAge,
                        Collectors.toMap(Person::getIncome, Function.identity())));

仅供参考 - 这是我使用的Person类。请注意equalshashcode已实施。

class Person {
    private final String name;
    private final int age;
    private final int income;


    public Person(String name, int age, int income) {
        this.name = name;
        this.age = age;
        this.income = income;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getIncome() {
        return income;
    }

    @Override
    public String toString() {
        return "Person{" + "name=" + name + ", age=" + age + ", income=" + income + '}';
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 59 * hash + Objects.hashCode(this.name);
        hash = 59 * hash + this.age;
        hash = 59 * hash + this.income;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Person other = (Person) obj;
        if (this.age != other.age) {
            return false;
        }
        if (this.income != other.income) {
            return false;
        }
        if (!Objects.equals(this.name, other.name)) {
            return false;
        }
        return true;
    }
}

答案 2 :(得分:2)

你应该看一下 Collectors.groupingBy()

        List<Person> persons = new ArrayList<>();
        Map<Integer, Map<Integer, List<Person>>> map = persons.stream().collect(Collectors.groupingBy(person -> person.getAge(),Collectors.groupingBy(person -> person.getIncome())));

这应该做你的事。

答案 3 :(得分:1)

为什么不(我认为你的类型是人,而不是人,你同时使用)

  for (person p : persons)
   {
       if (!results.containsKey(p.getAge())
             results.put(p.getAge(), new HashMap<income,persons>());
       results.get(p.getAge()).put(p.getIncome(), p);
   }

答案 4 :(得分:0)

我是第二个YaRiK; Guava Table<Integer, Integer, Set<Person>>在这里可以很好地运作。您应该使用Set<Person>来避免冲突,例如user902383建议。流API并不总是正确的工具,这在我看来就像传统的迭代循环much easier to read

试试这个:

Table<Integer, Integer, Set<Person>> table = HashBasedTable.create();
for (Person p : persons) {
  Set<Person> s = table.get(p.getAge(), p.getIncome());
  if (s == null) {
    s = new HashSet<>();
    table.put(p.getAge(), p.getIncome(), s);
  }
  s.add(p);
}

答案 5 :(得分:0)

您还可以实现自己的收集器来实现此目的。在番石榴的帮助下,您可以在一行中完成:

Map<Integer, Map<Integer, Person>> result = persons.stream().collect(HashMap::new, (store, person) -> store.put(person.getAge(), ImmutableMap.of(person.getIncome(), person)), HashMap::putAll);
相关问题