【问题标题】:How to rearrange items in list accordingly to a given pattern?如何根据给定的模式重新排列列表中的项目?
【发布时间】:2016-10-16 07:05:03
【问题描述】:

我有 id 列表,如下所示:5, 3, 2, 4, 1,然后我使用该 id 从 DB 中查询项目。但它们来自 DB 通常按 id 排序,我的意思是 1, 2, 3, 4, 5。我需要将它们重新排列为与它们的 ID 列表相同的顺序。我怎样才能做到这一点?

编辑:澄清一下,因为很多人似乎对这个问题感到困惑。 ID列表用于查询数据库,例如:

SELECT * FROM Foo WHERE id in (5, 3, 2, 4, 1)

Foo 对象的结果列表与 ID 列表的排序方式不同。问题是如何获取与初始 ID 列表顺序相同的 Foo 对象列表。

【问题讨论】:

  • 把你的问题看了5遍还是没看懂你的问题
  • 不要使用ORDER BY?向我们展示您的尝试。
  • 如果您在数据库中为 ID 5 进行选择,数据库将返回 ID 5 的数据。您是否同时请求不同 ID 的数据?
  • 我不明白反对票,这是一个很好的问题!唯一缺少的是您自己尝试过的代码示例。
  • 由于为问题选择的标签,不清楚您是否想要一种让数据库按顺序返回行的方法,或者您是否想在之后重新排序列表。在后一种情况下,所有这些特定于数据库的标签都已过时且具有误导性。

标签: java hibernate postgresql java-8 spring-data


【解决方案1】:

我认为您必须在代码中重新排序查询结果:

public static void main(String[] args) {

    List<Integer> ids = Arrays.asList(5, 3, 2, 4, 1, 6);
    List<Foo> results = Arrays.asList(new Foo(1), new Foo(8), new Foo(2), new Foo(4), new Foo(5), new Foo(7));

    System.out.println("sortResults1: " + sortResults1(ids, results));
    System.out.println("sortResults2: " + sortResults2(ids, results));
}

private static List<Foo> sortResults1(List<Integer> ids, List<Foo> results) {
    Foo[] sortedResultsArray = new Foo[ids.size()];
    for (Foo result : results) {
        // look up the required position of this result's id 
        int pos = ids.indexOf(result.getId());
        if (pos >= 0) {
            sortedResultsArray[pos] = result;
        }
    }
    List<Foo> sortedResults = new ArrayList<>(Arrays.asList(sortedResultsArray));
    sortedResults.removeAll(Collections.singleton(null));
    return sortedResults;
}

private static List<Foo> sortResults2(List<Integer> ids, List<Foo> results) {
    Collections.sort(results, Comparator.comparingInt(item -> ids.indexOf(item.getId())));
    return results;
}

第一个解决方案会省略 ID 列表中未出现的 ID 的任何结果。

使用比较器的第二种解决方案将任何 ID 未知的结果放在结果列表的前面。

输出:

sortResults1: [Foo 5, Foo 2, Foo 4, Foo 1]
sortResults2[Foo 8, Foo 7, Foo 5, Foo 2, Foo 4, Foo 1]

注意:在这里发现了一个类似的问题:Sort an (Array)List with a specific order TreeMap 有一个合理的答案。

【讨论】:

  • 您不能使用int[] 数组从int[] 数组创建list,但无论如何手动创建数组已经过时,只需使用List&lt;Integer&gt; ids = Arrays.asList(5, 3, 2, 4, 1);。顺便说一句,您可以使用sortedResults.removeAll(Collections.singleton(null));sortedResults.removeIf(Objects::isNull); 删除所有null 元素
  • 不需要创建数组也适用于另一个asList 调用:results = Arrays.asList(new Foo(1), new Foo(8), …。此外,您可以将不使用null 的数组转换为列表作为流操作:return Arrays.stream(sortedResultsArray) .filter(Objects::nonNull) .collect(Collectors.toList());
  • 我远离流,尤其是在其他人可以理解的代码中;-) 再次感谢您的指点。
【解决方案2】:

使用纯 PostgreSQL,

版本 >= 9.5:

select * from foo where id = any(array[5,3,2,4,1])
order by array_position(array[5,3,2,4,1], id);

版本 >= 9.4:

with lst(i,n) as (select * from unnest(array[5,3,2,4,1]) with ordinality)
select foo.* from foo join lst on (foo.id = lst.i)
order by lst.n

版本 >= 8.4:

with lst(i,n) as (select *, row_number() over () from unnest(array[5,3,2,4,1]))
select foo.* from foo join lst on (foo.id = lst.i)
order by lst.n

【讨论】:

  • @AdriaanKoster 这取决于业务逻辑的位置。例如,如果主查询是由 Java 代码自动生成的,那么 Java 解决方案会更好。
【解决方案3】:

如果您只想对列表进行排序5, 3, 2, 4, 1,那么您可以使用

Collections.sort(List);

如果您想根据其他列表对列表进行排序,那么您应该使用比较器,例如

Collections.sort(secondList, new Comparator<Item>() {
public int compare(Item left, Item right) {
    return Integer.compare(firstList.indexOf(left.getId(), firstList.indexOf(right.getId());
}
});

注意:- 这个比较器效率不高,你应该使用 hashmap 来存储索引。

【讨论】:

  • 比较器的好主意。但是 firstList 包含 ID,对吗?如果是这样,那么您应该比较firstList,indexof(left.getId()), firstList,indexOf(right.getId())
  • 是的,但我只是举了一个例子,因为问题不是很清楚。无论如何,我会编辑我的答案。
  • 由于这是一个 Java 8 问题,您可以将比较器实现为Comparator.comparingInt(item -&gt; firstList.indexOf(item.getId()))。对于五个元素,没有理由担心性能,在这种情况下,hashmap 不会更快。
  • 对不起我的错误我没有看到java8标签,如果有更多元素我只是警告他。
猜你喜欢
  • 2021-10-05
  • 2013-05-10
  • 2015-10-12
  • 2019-04-06
  • 1970-01-01
  • 2019-07-09
  • 2018-04-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多