【问题标题】:Sort object List by another List using Java Comparators使用 Java 比较器按另一个列表对对象列表进行排序
【发布时间】:2019-02-06 18:05:46
【问题描述】:

有两个输入列表如下:

inputA = [
            {
               name: "A",
               age: 20
            }, 
            {
               name: "B",
               age: 30
            },
            {  name: "C",
               age: 25
            },
            {  name: "D",
               age: 28
            }
          ]

inputB = ["D", "B"]

我的首选输出列表必须如下:

expectedOutput = [
            {
               name: "D",
               age: 28
            }, 
            {
               name: "B",
               age: 30
            },
            {  name: "A",
               age: 20
            },
            {  name: "C",
               age: 25
            }
          ]

到目前为止我所做的如下所示:

AtomicInteger count = new AtomicInteger();
Collections.sort(inputA, Comparator
    .comparing(a -> 
    if (inputB.indexOf(a.getName()) > -1) {
        return -1;
    }
    else {
        return count.incrementAndGet();
    })
    .thenComparingInt(a -> a.getAge()));

我得到的输出如下

actualOutput = [
            {
               name: "D",
               age: 28
            }, 
            {
               name: "B",
               age: 30
            },
            {  name: "C",
               age: 25
            },
            {  name: "A",
               age: 20
            }
          ]

问题在于列表inputB 中没有名称的元素。那里的订单在inputA 中没有原始订单。要使原始订单保持不变,{ name: "A", age: 20 } 应该在 { name: "C", age: 25 } 之前

在使用比较器链接策略时如何解决此问题?

更新 排序逻辑是,如果 inputA 具有名称等于 inputB 列表的对象,则这些元素应位于 inputA 的顶部,然后这些元素必须按其年龄排序,同时保持 inputA 中其他元素的原始顺序输入B中不存在

这不可能是重复的,因为这个问题试图比较两个列表,并根据第一个列表中对象的属性对公共元素进行排序,同时将其余元素保持原始顺序。

【问题讨论】:

  • 应该是.thenComparingInt 而不是comparingInt
  • 我很想知道在其他情况下使用return count.incrementAndGet(); 进行处理的原因是什么。问题出在哪里
  • 您订购商品的规则是什么?
  • @Joe 这怎么可能与您所说的问题重复?只需正确阅读问题,并将此问题的两个公认答案与您所指的问题进行比较

标签: java lambda java-8 java-stream comparator


【解决方案1】:

在我看来,如果名称包含在 inputB 列表中,您需要按年龄对元素进行排序,如果它们不包含在 inputB 列表中,则保留其余元素不变。按年龄排序的元素应该出现在结果的顶部,而未排序的元素应该出现在底部。

如果这是您需要做的,您可以使用Comparator.comparingInt 并让它返回一个整数,即年龄(对于第一种情况)或Integer.MAX_VALUE(对于另一种情况)。

您应该优化检查inputB,使其更快。为此,您可以从 inputB 创建一个 HashSet

这是代码:

Set<String> set = new HashSet<>(inputB);

Collections.sort(inputA, Comparator.comparingInt(a -> set.contains(a.getName()) ? 
                                                      a.getAge() : 
                                                      Integer.MAX_VALUE));

只要您的年龄不等于Integer.MAX_VALUE,此方法有效。

这个想法是你总是按年龄比较,但如果一个元素不属于inputB,你把年龄变成Integer.MAX_VALUE。这会有两个效果:第一,它会使inputB中不包含的元素出现在底部;其次,由于您总是返回Integer.MAX_VALUE,因此inputA 列表的顺序被保留,因为Collections.sort 实现了稳定排序

【讨论】:

    【解决方案2】:

    您的代码中的问题是第二个比较器在inputA 中的所有项目上都被激活。这意味着,即使不在inputB 中的项目也会按照inputA 的原始顺序进行排序。你可以这样避免:

    Collections.sort(inputA, Comparator.comparing(a ->
    {
        if (inputB.indexOf(a.getName()) > -1)
        {
            return 0;
        }
        return 1;
    })
    .thenComparingInt(a -> 
    {
       if (inputB.indexOf(a.getName()) > -1)
       {
           return a.getAge();
       }
       return Integer.MAX_VALUE;
    }));
    

    但是,如果inputB 未排序,我不确定是否需要特别调用两次IndexOf()。可能值得查看生成的字节码,看看它是否得到优化。

    有一件事是,使用计数对比较没有任何影响。您只需要在比较操作中返回 -1、0 或 1。因此,对计数进行线程安全递增没有任何价值。

    另一方面,如果可以,请在定义inputA 中的对象的类中引入priority 字段。然后你可以绕过多次调用indexOf() 的问题,例如:

    Collections.sort(inputA, Comparator.comparing(a ->
    {
       if (inputB.indexOf(a.getName()) > -1)
       {
           a.setPriority(a.getAge());
           return 0;
       }
    
       a.setPriority(Integer.MAX_VALUE);
       return 1;
    })
    .thenComparingInt(a -> a.getPriority()));
    

    【讨论】:

      猜你喜欢
      • 2018-09-10
      • 2021-11-06
      • 2014-12-10
      • 2013-02-13
      • 1970-01-01
      • 2012-01-15
      • 2023-04-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多