【问题标题】:Sort a Collection of POJOs based on a List of integers根据整数列表对 POJO 集合进行排序
【发布时间】:2013-08-05 22:55:53
【问题描述】:

所以,

我有一种情况,我使用整数列表从不同来源获取一些数据。结果,我得到了两个列表,它们都具有与内容相同的 POJO 类型,并且包含了输入列表中的所有元素,但是顺序未知。

我需要的是让这些 POJO 按照与我的第一个列表相同的顺序排序(这个 Integer 是 POJO 中带有 getter/setter 的字段),所以我得到一个包含 POJO 的列表,其顺序与我的 Integer 完全相同列表。

所以,我正在考虑的步骤是:

  1. 将两个 POJO 列表合二为一,因此我至少有 2 个大小相同的列表(整数和 POJO),我知道它们包含匹配的元素。
  2. 对生成的 POJO 列表进行排序以匹配整数列表。

但是,对于第 2 步,我需要找到好的(即高效且整洁的)方法来完成它...我正在考虑创建一个类似这样的比较器:

public class POJOComparable implements Comparator<MyPOJO>{

  private List<Integer> values;

  public POJOComparable(List<Integer> values) {
    this.values = values;
  } 

  @Override
  public int compare(MyPOJO o1P, MyPOJO o2P) {
      int o1 = values.indexOf(o1P.getId());
      int o2 = values.indexOf(o2P.getId());
      return (o1>o2 ? -1 : (o1==o2 ? 0 : 1));
  }
} 

现在,这是进行此类排序的好方法还是有更好或更有效的方法?列表将包含大约 20 个项目,但这种排序会经常执行,所以我正在寻找有效的方法。

【问题讨论】:

  • List&lt;MyPojo&gt; 的值存储在TreeMap&lt;Integer, MyPojo&gt; 中,并让这个集合为您进行排序。
  • 我不太明白。你有一个List&lt;Integer&gt; 和一个List&lt;Pojo&gt; 并且你想匹配这些元素?或者只是对List&lt;Pojo&gt; 进行排序?
  • 好吧,不得不说,List 可能包含这样的内容:(5, 7, 2, 35, 21, 78, 1),这是我期望的正确顺序TreeMap,如果我想将第一个 Collection 的索引存储在 TreeMap 中,我仍然必须使用第一个 List 中的值构造 TreeMap。那会比我的方法更有效吗?
  • @BoristheSpider,这个想法是匹配两个列表的顺序。在 POJO 中,我有一个与整数列表的值匹配的整数字段,但是我的整数列表的顺序不同。

标签: java sorting collections


【解决方案1】:

List&lt;MyPojo&gt; 的值存储在TreeMap&lt;Integer, MyPojo&gt; 中,并让该集合为您进行排序。一个简单的方法是:

TreeMap<Integer, MyPojo> aSortedMap = new TreeMap<Integer, MyPojo>();
for(MyPojo pojo : aListOfMyPojo) {
    aSortedMap.put(values.indexOf(pojo.getId()), pojo);
}

请注意,此方法假定每个 pojo.getId()List&lt;Integer&gt; values 内都有一个唯一值。


基于@BoristheSpider 评论,与其拥有List&lt;Integer&gt; values 并使用List#indexOf 查找pojo.getId() 的每个值,不如使用Map&lt;Integer, Integer&gt; 来存储pojo.getId() 的值和所需的值Integer 值可加速搜索过程。因此,生成的算法应如下所示:

//replace the List<Integer> by a Map<Integer>
Map<Integer, Integer> values = new HashMap<Integer, Integer>();
//store the pojo.getId() value with its associated int value
values.put(pojo.getId(), ...);

//...
TreeMap<Integer, MyPojo> aSortedMap = new TreeMap<Integer, MyPojo>();
for(MyPojo pojo : aListOfMyPojo) {
    aSortedMap.put(values.get(pojo.getId()), pojo);
}

【讨论】:

  • 好的,看起来是个不错的方法。好像我只为元素调用一次 indexOf,所以它应该是比使用 Comparator 更好的方法......
  • 这太可怕了。 indexofO(n)List 所以这整件事是 O(n^2) 应该是 O(n)
  • @BoristheSpider 但这就是 OP 希望/需要进行排序的方式。注意values.indexOf(pojo.getId()) 可以返回10pojo.getId() 返回0
  • 是的,这就是为什么我建议使用 Map 按 id 索引 pojos 以便稍后查找。该方法是O(n),因为putgetO(1),对于HashMap
  • @BoristheSpider 根据您的方法,而不是Map&lt;Integer, Pojo&gt;,它应该是Map&lt;Integer, Integer&gt;。我会更新我的答案。
【解决方案2】:

排序很昂贵,需要O(nlgn)。我会说使用Map 按id 映射Pojos,然后在List 顺序中从Map 中检索它们。这需要更多的内存,但应该更快:

    final Map<Integer, Pojo> pojoMap = new HashMap<>(pojos.size(), 1f);
    for (final Pojo pojo : pojos) {
        pojoMap.put(pojo.id, pojo);
    }

    final List<Pojo> sortedPojos = new ArrayList<>(ids.size());
    for (final int id : ids) {
        sortedPojos.add(pojoMap.get(id));
    }

所以

  1. 遍历Pojos 并通过属性创建Map
  2. 通过在Map 中查找Pojos 来遍历原始List 并填充新的List

【讨论】:

  • 好吧,因为我喜欢在实践中确认答案,而不仅仅是在理论上,我使用 System.nanoTime() 在超过 500 个元素的列表上测量了时间。您的解决方案平均约 278000 纳秒,来自 Luiggi 2111000 的解决方案,因此您的解决方案在实践中比我将使用的列表要快 8 倍。我认为我可以同时使用这两种方法,并且对于小列表(20 个元素而不是 507 个)来说会很好,但是我真的更喜欢控制我作为输出得到的列表类型,并且采用更有效的方式总是更好。谢谢你们的努力!
  • 注意:即使在20个元素的列表上,也至少快一倍,所以真的可以确认是O(n),另一个是O(n^2)。使用 Collection 框架有时并不那么容易看到,您必须真正信任实现并了解每个 Collection 的处理方式......
【解决方案3】:

更改 POJO 类以将索引包含为 int。如果您一次只订购数十个对象,那么浪费的空间是微不足道的。好处是简单、直接,并降低了混淆的风险。

比较器只查看每个对象中的一个字段 - 没有昂贵的列表搜索。

【讨论】:

  • 对不起,这个不行,Pojo是在另外一层生成的,所以我当时没有索引值。
【解决方案4】:

分而治之,首先对两个列表进行独立排序,然后将它们合并在一起,每次迭代时比较两个列表的第一个元素(使用比较器)。这是最有效的方法。

您可以使用默认的 java 排序对两个列表进行排序。

【讨论】:

  • 作为一个问题,这种方法将使用我正在构建的比较器?对于合并,您会使用什么?
  • 一个简单的自写循环,检查第一个元素中哪个具有最小值。这就是合并排序的工作原理。
猜你喜欢
  • 2015-12-12
  • 2015-10-09
  • 1970-01-01
  • 2020-02-28
  • 2016-01-06
  • 2022-07-19
  • 2021-04-23
  • 2012-11-07
  • 1970-01-01
相关资源
最近更新 更多