添加两个自己类型的列表

时间:2019-01-02 17:36:11

标签: java list for-loop collections

我有一个简单的User类,具有一个String和一个int属性。

我想以此方式添加两个用户列表:

  • 如果字符串等于,则应添加数字,这将 成为其新价值。
  • 新列表应包括所有具有正确值的用户。

赞:

List1: { [a:2], [b:3] }
List2: { [b:4], [c:5] }
ResultList: {[a:2], [b:7], [c:5]}

User定义:

public class User { 
    private String name;
    private int comments;
}

我的方法:

public List<User> addTwoList(List<User> first, List<User> sec) {
    List<User> result = new ArrayList<>();
    for (int i=0; i<first.size(); i++) {
        Boolean bsin = false;
        Boolean isin = false;
        for (int j=0; j<sec.size(); j++) {
            isin = false; 
            if (first.get(i).getName().equals(sec.get(j).getName())) {
                int value= first.get(i).getComments() + sec.get(j).getComments();
                result.add(new User(first.get(i).getName(), value));
                isin = true;
                bsin = true;
            }
            if (!isin) {result.add(sec.get(j));}
        }
        if (!bsin) {result.add(first.get(i));}
    }
    return result;      
}

但是它将很多东西添加到列表中。

4 个答案:

答案 0 :(得分:5)

最好通过toMap收集器来完成此操作:

 Collection<User> result = Stream
    .concat(first.stream(), second.stream())
    .collect(Collectors.toMap(
        User::getName,
        u -> new User(u.getName(), u.getComments()),
        (l, r) -> {
            l.setComments(l.getComments() + r.getComments());
            return l;
        }))
    .values();
  • 首先,通过Stream<User>将两个列表连接成一个Stream.concat
  • 第二,我们使用toMap收集器合并碰巧具有相同Name的用户,并返回Collection<User>的结果。

如果您严格想要一个List<User>,然后将结果传递到ArrayList构造函数中,即List<User> resultSet = new ArrayList<>(result);


对@davidxxx表示敬意,您可以直接从管道中收集到列表,并避免使用以下方法创建中间变量:

List<User> result = Stream
    .concat(first.stream(), second.stream())
    .collect(Collectors.toMap(
         User::getName,
         u -> new User(u.getName(), u.getComments()),
         (l, r) -> {
              l.setComments(l.getComments() + r.getComments());
              return l;
         }))
    .values()
    .stream()
    .collect(Collectors.toList());

答案 1 :(得分:3)

您必须使用中间映射来通过汇总年龄将两个列表中的用户合并

一种方法是使用流,如Aomine's answer所示。这是没有流的另一种方法:

Map<String, Integer> map = new LinkedHashMap<>();
list1.forEach(u -> map.merge(u.getName(), u.getComments(), Integer::sum));
list2.forEach(u -> map.merge(u.getName(), u.getComments(), Integer::sum));

现在,您可以创建用户列表,如下所示:

List<User> result = new ArrayList<>();
map.forEach((name, comments) -> result.add(new User(name, comments)));

这假设User的构造函数可以接受namecomments


编辑:如@davidxxx所建议,我们可以通过剔除第一部分来改进代码:

BiConsumer<List<User>, Map<String, Integer>> action = (list, map) -> 
        list.forEach(u -> map.merge(u.getName(), u.getComments(), Integer::sum));

Map<String, Integer> map = new LinkedHashMap<>();
action.accept(list1, map);
action.accept(list2, map);

此重构将避免DRY。

答案 2 :(得分:2)

使用Collectors.groupingByCollectors.reducing的一种很直接的方法,不需要,需要使用setter,这是最大的优势,因为您可以保留User 不变

Collection<Optional<User>> d = Stream
    .of(first, second)                    // start with Stream<List<User>> 
    .flatMap(List::stream)                // flatting to the Stream<User>
    .collect(Collectors.groupingBy(       // Collecting to Map<String, List<User>>
        User::getName,                    // by name (the key)
                                          // and reducing the list into a single User

        Collectors.reducing((l, r) -> new User(l.getName(), l.getComments() + r.getComments()))))
    .values();                            // return values from Map<String, List<User>>

不幸的是,由于归约管道会返回Collection<Optional<User>>,因此结果为Optional,因为结果可能根本不存在。您可以流式传输值并使用map()来摆脱Optional或使用Collectors.collectAndThen *:

Collection<User> d = Stream
    .of(first, second)                    // start with Stream<List<User>> 
    .flatMap(List::stream)                // flatting to the Stream<User>
    .collect(Collectors.groupingBy(       // Collecting to Map<String, List<User>>       
        User::getName,                    // by name (the key)
        Collectors.collectingAndThen(     // reduce the list into a single User

            Collectors.reducing((l, r) -> new User(l.getName(), l.getComments() + r.getComments())), 
            Optional::get)))              // and extract from the Optional
    .values();  

*感谢@ Aomine

答案 3 :(得分:2)

作为一种相当直接和有效的选择:

  • 流元素
  • 将它们收集到Map<String, Integer>中,以将每个名称与评论总数(int
  • 流式传输所收集地图的条目以创建“用户列表”。

或者,对于第三步,您可以使用MapcollectingAndThen(groupingBy()..., m -> ...收集器进行精加工转换 但是我并不总是很可读,在这里我们可以没有。

它将给出:

List<User> users =
        Stream.concat(first.stream(), second.stream())
              .collect(groupingBy(User::getName, summingInt(User::getComments)))
              .entrySet()
              .stream()
              .map(e -> new User(e.getKey(), e.getValue()))
              .collect(toList());