按特定逻辑合并 ArrayList of Integers

Posted

技术标签:

【中文标题】按特定逻辑合并 ArrayList of Integers【英文标题】:Merge ArrayList of Integers by specific logic 【发布时间】:2019-05-05 21:33:18 【问题描述】:

我的 ArrayList 包含我的简单模型类 User 的许多实例,其中仅包含 2 个字段:

@Data
public class User 
    private String email;
    private ArrayList<Integer> lists; 
    

我在 lists 字段中插入整数值,总是 10User 类有许多对象,其中一些是重复的,因为它们具有相同的 email 地址但不同的列表

我需要将 Users 的重复项合并到 User 的一个对象中,同时还要注意 lists 字段。

User user1 = new User('email@gmail.com', Arrays.asList(0, 1, 0, 1, 1)); 
User user2 = new User('email@gmail.com', Arrays.asList(0, 0, 0, 1, 1));
User user3 = new User('email@gmail.com', Arrays.asList(1, 1, 1, 1, 1));
/* merge duplicated objects into one */
User mergedUser = new User('email@gmail.com', Arrays.asList(1, 1, 1, 1, 1));

我在实现将许多 列表 合并为一个的逻辑时遇到了困难。 其背后的逻辑并不复杂:只要有1,就将1放入合并列表。有许多具有 0 且只有一个 1 的列表,会在最终合并列表中产生值 1

我应该采取什么方法来实现这个合并列表的逻辑?

【问题讨论】:

【参考方案1】:

从JDK8开始,你可以使用toMap方法来完成你的任务。

Collection<User> result = 
        list.stream()
           .collect(toMap(User::getEmail,
                   Function.identity(),
                   (l, r) -> 
                       List<Integer> firstList = l.getList();
                       List<Integer> secondList = r.getList();
                     IntStream.range(0, firstList.size())
                              .forEach(i -> firstList.set(i, Math.max(firstList.get(i), secondList.get(i))));
                            return l;
                   )).values();

您可以通过将合并逻辑提取到辅助方法中来进一步使其更具可读性:

private static User apply(User l, User r) 
        List<Integer> firstList = l.getList();
        List<Integer> secondList = r.getList();
        IntStream.range(0, firstList.size())
                .forEach(i -> firstList.set(i, Math.max(firstList.get(i), secondList.get(i))));
        return l;

那么你可以这样做:

Collection<User> result = list.stream()
                .collect(toMap(User::getEmail, Function.identity(), Main::apply))
                .values();

其中Main 指的是包含apply 方法的类。


注意这里重要的是合并函数 ((l, r) -&gt; ...),see this answer 稍微解释了合并函数。

您可能需要使用toMap 收集器来了解my other posts 以熟悉它和ofcourse the API doc。

【讨论】:

【参考方案2】:

你可以这样做:

List<User> finalList = new ArrayList<>(users.stream()
        .collect(Collectors.toMap(User::getEmail, Function.identity(), (user1, user2) -> 
            List<Integer> l1 = user1.getLists();
            List<Integer> l2 = user2.getLists();
            List<Integer> merge = IntStream.range(0, l1.size())
                    .mapToObj(i -> (l1.get(i) == 0 && l2.get(i) == 0) ? 0 : 1)
                    .collect(Collectors.toList());
            return new User(user1.getEmail(), merge);
        )).values());

【讨论】:

【参考方案3】:

List的值总是0或者1,我推荐使用long或者long[]而不是ArrayList

@Data
@AllArgsConstructor
public class User 
  private String email;
  private long flags;

  public static long merge(long... flags) 
    long result = 0;
    for (long flag : flags) 
      result = result | flag;
    
    return result;
  


  // test
  public static void main(String[] args) 
    User user1 = new User("email@gmail.com", Long.valueOf("1000000000000101",2));
    User user2 = new User("email@gmail.com", Long.valueOf("0000111100000101",2));
    User user3 = new User("email@gmail.com", Long.valueOf("0000000010110101",2));
    System.out.println(Long.toBinaryString(merge(user1.flags, user2.flags, user3.flags)));
    // result is 1000111110110101
  

如果flags的数量大于32,使用long[]保存更多的flags;

【讨论】:

我无法理解这会给mergedUser 提供什么,如 OP 所示? 用户 mergeUser = new User("email@gmail.com", merge(user1.flags, user2.flags, user3.flags));

以上是关于按特定逻辑合并 ArrayList of Integers的主要内容,如果未能解决你的问题,请参考以下文章

酒店预订可能

按特定参数对对象列表进行排序

Groovy:如何按字符串长度顺序对 String:s 的 ArrayList 进行排序?

ArrayList 按 Id 检索对象

按特定属性对类列表进行排序[重复]

如何合并具有特定移位(偏移)的两个位图?