【问题标题】:Matching 2 ArrayLists of objects based on their member variables根据成员变量匹配 2 个对象的 ArrayList
【发布时间】:2019-12-24 16:56:43
【问题描述】:

假设我有 2 个对象,Object1 和 Object2。它们的基本结构如下:

对象 1

int id
String email

对象 2

int id
ArrayList<String> emails

现在我有 2 个 ArrayList,Object1 和 Object2 之一。找到对象 1 的电子邮件包含在对象 2 的电子邮件 ArrayList 中的匹配项,然后将它们的 id 存储在 HashMap(或任何其他包含 2 个整数的数据结构)中的有效方法是什么?

我知道显而易见且基本的解决方案是用 2 个 for 循环强制它,如下所示:

ArrayList<Object1> obj1List;
ArrayList<Object2> obj2List;
HashMap<Integer, Integer> idMapping = new HashMap()<>;

for (Object1 obj1 : obj1List){
    String obj1Email = obj1.getEmail();
    for (Object2 obj2 : obj2List){
        ArrayList<String> obj2EmailList = obj2.getEmails();
        if(obj2EmailList.contains(obj1Email)){
            int obj1Id = obj1.getId();
            int obj2Id = obj2.getId();
            idMapping.put(obj1Id, obj2Id);
        }
    }
}

每个 ArrayList 都有大约一千个对象,因此性能确实不是什么大问题。但是,我确信有更优雅的方法可以解决这个问题。我猜可能使用流,但我对它们还不够熟悉,无法做到这一点。有什么建议吗?

【问题讨论】:

  • 如果您坚持使用ArrayList,那么从性能的角度来看,这与您将获得的一样好。流可能会使代码更短,但不会使其更快。
  • 保留迭代解决方案,它更具可读性并且优于流解决方案。

标签: java arraylist java-stream


【解决方案1】:

我认为你的做法绝对没问题。不过,如果你想使用streams,你可以试试这个方法。

由于您要为每个Object1 频繁地遍历ListObject2,我建议为Object2 List 创建一个Map,以便您的检索更快。

根据您的代码,我想我可以将每封电子邮件映射到它的个人 ID 假设 email in Object2 List 可以是 never have more than 1 id,我认为是 true 根据您的实现.

如果我的假设是错误的,我们无能为力。

代码如下:

Map<String, Integer> obj2Map = new HashMap<>();
for (Object2 obj2 : obj2List) {
    int id = obj2.getId();
    obj2Map.putAll(
            obj2.getEmails()
                    .stream()
                    .collect(Collectors.toMap(String::toString, email -> id))
    );
}

Map<Integer, Integer> idMapping = new HashMap();
for (Object1 obj1 : obj1List) {
    if (obj2Map.containsKey(obj1.getEmail())) {
        idMapping.put(obj1.getId(), obj2Map.get(obj1.getEmail()));
    }
}

【讨论】:

  • 我建议用for (String email : obj2.getEmails()) obj2Map.put(email, obj2.getId());替换第一个循环中的奇怪代码。
【解决方案2】:
Map<Integer, Integer> collect = object1List.stream()
                .flatMap(ob1 -> object2List.stream().filter(ob2 -> ob2.getEmails().contains(ob1.getEmail())).map(ob2 -> {
                    int [] arr = new int[2];
                    arr[0] = ob1.getId();
                    arr[1] = ob2.getId();
                    return arr;
                }))
                .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1]));

您可以使用 flatMap 将第二个对象中的电子邮件列表展平,并使用 ArrayList contains 方法与第一个对象进行比较。

以上代码是您的代码的流版本,但地图中可能存在重复键。

【讨论】:

    【解决方案3】:

    作为idMapping,我建议使用Map&lt;Integer, List&lt;Integer&gt;&gt;obj2List 中可能有更多 id 与同一电子邮件。以这个数据为例:

    List<Object1> obj1List = Arrays.asList(
            new Object1(1, "a"),
            new Object1(2, "b"),
            new Object1(3, "c"));
    List<Object2> obj2List = Arrays.asList(
            new Object2(11, "a"),
            new Object2(12, "a", "b"),
            new Object2(14, "c", "d"),
            new Object2(15, "e", "f")
            );
    

    HashMap&lt;Integer, Integer&gt; idMapping 你得到:

    {1=12, 2=12, 3=14}
    

    但对于Map&lt;Integer, List&lt;Integer&gt;&gt; idMapping,它将是:

    {1=[11, 12], 2=[12], 3=[14]}
    

    主要解决方案可能如下所示。创建地图(通过电子邮件分组),然后使用它创建idMapping

    Map<String, List<Integer>> idsForEmailFromObj2List = obj2List.stream().
            flatMap(obj2 -> obj2.getEmails().stream().map(
                    email -> new Object1(obj2.getId(), email)
            ))
            .collect(Collectors.groupingBy(
            Object1::getEmail,
            Collectors.mapping(Object1::getId, Collectors.toList())));
    
    Map<Integer, List<Integer>> idMapping = obj1List.stream()
            .filter(obj1 -> idsForEmailFromObj2List.containsKey(obj1.getEmail()))
            .collect(Collectors.toMap(
                    Object1::getId, 
                    obj1 -> idsForEmailFromObj2List.get(obj1.getEmail())));
    

    【讨论】:

      猜你喜欢
      • 2015-02-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-02
      • 2014-12-29
      • 2013-03-10
      • 1970-01-01
      相关资源
      最近更新 更多