使用Java比较器按另一个列表对对象列表进行排序

时间:2019-02-06 18:05:46

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

有两个输入列表,如下所示:

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中其他元素的原始顺序,在inputB中不存在

这不可能重复,因为此问题试图比较两个列表,并且还按照第一个列表中对象的属性对公共元素进行排序,而其余元素保持其原始顺序。

2 个答案:

答案 0 :(得分:3)

如我所见,如果名称包含在finished列表中,则需要按年龄对元素进行排序,如果其余元素不包含在{{1}中,则需要保留其余元素}列表。按年龄排序的元素应显示在结果的顶部,而未排序的元素应显示在结果的底部。

如果这是您需要执行的操作,则可以使用select * from tbl where arabic_field <> convert(varchar(8000), arabicfield) --this may be omitted and arabic-field like '%['+nchar(0x0600)+'-'+nchar(0x06FF)+']%' --checks if contains at least an arabic char (unicode 0600–06FF) 并让它返回一个整数,该整数是age(对于第一种情况)或inputB(对于另一种情况)。

您应该优化对inputB的检查,以使其快速。为此,您可以从Comparator.comparingInt创建一个Integer.MAX_VALUE

这是代码:

inputB

只要您的年龄不等于HashSet,就可以使用此功能。

这个想法是,您总是按年龄进行比较,但是如果某个元素不属于inputB,则会将年龄变成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实现了 stable sort

答案 1 :(得分:1)

您的代码中的问题是第二个比较器在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;
}));

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

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

另一方面,如果可以的话,请在用于定义priority中的对象的类中引入一个inputA字段。然后,您可以解决多次调用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()));