【问题标题】:How do I add up and remove repeated objects from ArrayList? [duplicate]如何从 ArrayList 中添加和删除重复的对象? [复制]
【发布时间】:2020-03-04 01:30:50
【问题描述】:

用户详细信息模型:

private String userName;
private int userSalary;

我有一个用户信息的 ArrayList

List<UserDetail> userDetails = new ArrayList<>();

UserDetail user1 = new UserDetail("Robert", 100);
UserDetail user2 = new UserDetail("John", 100);
UserDetail user3 = new UserDetail("Robert", 55);

userdetails.add(user1);
userdetails.add(user2);
userdetails.add(user3);

我正在尝试遍历数组并找出是否有任何基于userName 的重复条目,从上面的列表中我有两个具有相同用户名"Robert" 的记录,在这种情况下我想将userSalary 相加并从列表中删除一条记录。

预期的新 ArrayList:

userName userSalary

Robert 155
John 100

这可能吗??

【问题讨论】:

  • 您将相同的user1 添加到列表中的三倍。没事吧?
  • 您可以使用以 userName 为键、以 userSalary 为值的地图来帮助您汇总同一用户的薪水。
  • @Uata 更新了我的帖子,应该是 user1、user2 和 user3。
  • 这能回答你的问题吗? Java - Merge objects of list given a condition

标签: java java-8


【解决方案1】:
 userDetails.stream()
            .collect(Collectors.toMap(
                        UserDetail::getName,
                        Function.identity(),
                        (left, right) -> {
                            left.setSalary(left.getSalary() + right.getSalary());
                            return left;
                        }
                    ))
            .values();

这会给你一个Collection&lt;UserDetail&gt;。显然,如果需要,您可以将其复制到 ArrayList 中。

【讨论】:

    【解决方案2】:

    因为您的目标是将共享相同userNameUserDetail 对象分组,所以我建议将结果存储在Map 而不是ArrayList

    这可以通过流式传输您的List 并使用Collectors#groupingByCollectors#summingInt 将其收集到Map 来实现:

    List<UserDetail> userDetails = new ArrayList<>();
    
    UserDetail user1 = new UserDetail("Robert", 100);
    UserDetail user2 = new UserDetail("John", 100);
    UserDetail user3 = new UserDetail("Robert", 55);
    
    userDetails.add(user1);
    userDetails.add(user2);
    userDetails.add(user3);
    
    Map<String, Integer> groupedUserDetails = userDetails.stream()
        .collect(Collectors.groupingBy(UserDetail::getUserName,
            Collectors.summingInt(UserDetail::getUserSalary)));
    
    System.out.println(groupedUserDetails);
    

    上面这个sn-p可能会输出如下:

    {Robert=155, John=100}
    

    如果您想将此Map&lt;String, Integer&gt; 转换为List&lt;UserDetail&gt;,则可以使用以下内容:

    List<UserDetail> newUserDetails = groupedUserDetails.entrySet()
        .stream()
        .map(entry -> new UserDetail(entry.getKey(), entry.getValue())
        .collect(Collectors.toList());
    

    【讨论】:

    • 那是 Map&lt;String, Integer&gt;,而不是 OP 要求的 List/Collection&lt;UserDetail&gt;
    【解决方案3】:

    这是一个没有流的解决方案(可能更容易理解):

    Iterator<UserDetail> it=userDetails.iterator();
    Map<String,UserDetail> found=new HashMap<>();
    while(it.hasNext()){
        UserDetail next=it.next();
        if(found.containsKey(next.getUserName())){
            found.get(next.getUserName()).setUserSalery(found.get(next.getUserName()).getUserSalery()+next.getUserSalery();
            it.remove();
        }
        else{
            found.put(next.getUserName(),next);
        }
    }
    

    这会遍历所有元素。

    如果它已经找到了一个匹配的元素,它会向它添加自己的 salery 并将自己从列表中删除。

    如果没有,它会将自己标记为如果以后找到具有相同名称的其他元素。

    这假定UserDetail 具有userNameuserSalery 的标准getter/setter 方法。

    请注意,不能使用 for-each 循环,因为您无法在其中修改 List 的内容(它会抛出 ConcurrentModificationException)。

    来自cmets(来自@Holger:

    您可以使用单个UserDetail previous = found.putIfAbsent(next.getName());,后跟if(previous != null) { previous.setSalery(previous.getSalery()+next.getSalery()); it.remove(); },而不是连续查找地图三次。

    该代码将是:

    Iterator<UserDetail> it=userDetails.iterator();
    Map<String,UserDetail> found=new HashMap<>();
    while(it.hasNext()){
        UserDetail next=it.next();
        UserDetail previous = found.putIfAbsent(next.getUserName());
        if(previous != null) {
            previous.setUserSalery(previous.getUserSalery()+next.getUserSalery());
            it.remove();
        }
    }
    

    这基本上做同样的事情。

    如果当前元素不存在,它会将当前元素添加到列表中,如果不存在,它只会添加销售量。

    【讨论】:

    • 您可以使用单个UserDetail previous = found.putIfAbsent(next.getName());,后跟if(previous != null) { previous.setSalery(previous.getSalery()+next.getSalery()); it.remove(); },而不是连续查找地图三次。
    • @Holger 我已经添加了。
    【解决方案4】:

    我可以建议一种替代方法。哈希映射

    • 使用用户名作为键
    • 覆盖等于和哈希码
    • 在向 hashMap 添加元素之前,检查是否已经存在任何对象
    • 如果找到对象,则从中获取薪水,更新当前对象,然后添加到地图中

    public class UserDetail {
        private String userName;
        private int userSalary;
    
        @Override
        public boolean equals(Object obj) {
            return this.userName.equals(((UserDetail)obj).userName);
        }
    
        @Override
        public int hashCode() {
            return userName.length();
        }
    }
    

    【讨论】:

      【解决方案5】:

      这里Map 用于存储UserDetail by userName。如果用户在地图中,工资将在地图中添加和更新。其他明智的用户将被放入地图中。最后地图值将被转换为UserDetail 列表

      Map<String, UserDetail> mergedMap = new HashMap<>(); // username is used as the key 
      userDetails.forEach(userDetail -> {
         String userName = userDetail.getUserName();
         UserDetail userInMap = mergedMap.get(userName);
         if (userInMap != null) { // if user is in map salary will be added
            userInMap.setUserSalary(userInMap.getUserSalary() + userDetail.getUserSalary());
         } else { //otherwise user will put in map
            mergedMap.put(userDetail.getUserName(), userDetail);
         }
      });
      
      List<UserDetail> usersWithMergedSalaries = new ArrayList<>(mergedMap.values()); //convert map values to a list
      

      【讨论】:

        猜你喜欢
        • 2015-08-25
        • 1970-01-01
        • 1970-01-01
        • 2020-12-17
        • 2011-11-24
        • 1970-01-01
        • 2013-09-04
        • 2013-12-24
        • 2016-07-08
        相关资源
        最近更新 更多