首页 > 解决方案 > java - 如何对列表进行排序并使用其更改的索引对Java中的另一个列表进行排序?

问题描述

我有 3 个列表:

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4)); 
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

如果我排序at,我会得到[0, 2, 2, 3, 4]。
我想要两者btprc遵循这种模式。即:bt =[1, 2, 3, 5, 4]prc=["p2", "p1", "p3", "p4", "p5"]。由于在at排序时索引 1 与索引 2 交换,我希望其他 2 个数组也发生同样的事情。

我尝试在 Python 中实现它。这很容易,因为如果值在数据框中,对一列进行排序将自动给出所需的结果。我不能在 Java 中做同样的事情。

标签: javaarrayslistsorting

解决方案


方法一(先提取索引,构造新列表)

如果您可以更改数据格式并使用自定义对象,我建议使用方法 2。

您可以先将原始元素的索引提取at到一个数组sortedIndices中。如果列表已经排序,则值为[1, 2, 3, ..., n-1]. 如果需要交换前两个元素,则值为[1, 0, 2, 3, ..., n-1]. 之后,您可以通过根据存储的位置添加元素来构造和填充两个排序列表 ( btSorted/ ) 。prcSortedsortedIndices[i]

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4));
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

// get indexes of a sorted 'at' list
int[] sortedIndices = IntStream.range(0, at.size())
        .boxed().sorted(Comparator.comparing(at::get))
        .mapToInt(x -> x).toArray();

// sort 'bt' according to 'at'
ArrayList<Integer> btSorted = new ArrayList<>();
for (int i = 0; i < sortedIndices.length; i++) {
    btSorted.add(bt.get(sortedIndices[i]));
}

// sort 'prc' according to 'at'
ArrayList<String> prcSorted = new ArrayList<>();
for (int i = 0; i < sortedIndices.length; i++) {
    prcSorted.add(prc.get(sortedIndices[i]));
}

// sort 'at' directly (just for inspection)
ArrayList<Integer> atSorted = new ArrayList<>(at);
Collections.sort(atSorted);

System.out.println("idx: " + Arrays.toString(sortedIndices));
System.out.println("at:  " + at + " => " + atSorted);
System.out.println("bt:  " + bt + " => " + btSorted);
System.out.println("prc: " + prc + " => " + prcSorted);

输出:

idx: [1, 0, 2, 3, 4]
at:  [2, 0, 2, 3, 4] => [0, 2, 2, 3, 4]
bt:  [2, 1, 3, 5, 4] => [1, 2, 3, 5, 4]
prc: [p1, p2, p3, p4, p5] => [p2, p1, p3, p4, p5]

方法2 (自定义类和Comparator

更具可读性的是将数据存储在自定义类中(例如DataEntry)。然后,您可以轻松地对对象数组(例如data)进行排序,而无需使用难以阅读的代码:

ArrayList<DataEntry> data = new ArrayList<>();
data.add(new DataEntry(2, 2, "p1"));
data.add(new DataEntry(0, 1, "p2"));
data.add(new DataEntry(2, 3, "p3"));
data.add(new DataEntry(3, 5, "p4"));
data.add(new DataEntry(4, 4, "p5"));

// sort data by 'at'
data.sort(Comparator.comparing(d -> d.at));

// print result
data.forEach(d -> System.out.println(d.at + ", " + d.bt + ", " + d.prc));

输出:

0, 1, p2
2, 2, p1
2, 3, p3
3, 5, p4
4, 4, p5

DataEntry参考类:

static class DataEntry {
    int at;
    int bt;
    String prc;

    public DataEntry(int at, int bt, String prc) {
        this.at = at;
        this.bt = bt;
        this.prc = prc;
    }
}

方法 3 (使用indexOfComparator

这仅在要排序的列表中没有重复项(bt/ prc)时才稳定,因为indexOf始终返回列表中指定元素的第一次出现的索引(或 -1)。

可以使用自定义Comparator和对列表进行排序indexOf。我们可以在其他列表 ( / ) 中引用at当前被比较元素 ( ) 的相应位置的值,以便对它们进行排序:dbtprc

ArrayList<Integer> at = new ArrayList<>(Arrays.asList(2, 0, 2, 3, 4));
ArrayList<Integer> bt = new ArrayList<>(Arrays.asList(2, 1, 3, 5, 4));
ArrayList<String> prc = new ArrayList<>(Arrays.asList("p1", "p2", "p3", "p4", "p5"));

// sort 'bt' according to 'at'
ArrayList<Integer> btSorted = new ArrayList<>(bt);
btSorted.sort(Comparator.comparing(d -> at.get(bt.indexOf(d))));

// sort prc according to 'at'
ArrayList<String> prcSorted = new ArrayList<>(prc);
prcSorted.sort(Comparator.comparing(d -> at.get(prc.indexOf(d))));

// sort 'at' (just for inspection)
ArrayList<Integer> atSorted = new ArrayList<>(at);
Collections.sort(atSorted);

// print result
System.out.println(at + " => " + atSorted);
System.out.println(bt + " => " + btSorted);
System.out.println(prc + " => " + prcSorted);

输出:

at:  [2, 0, 2, 3, 4] => [0, 2, 2, 3, 4]
bt:  [2, 1, 3, 5, 4] => [1, 2, 3, 5, 4]
prc: [p1, p2, p3, p4, p5] => [p2, p1, p3, p4, p5]

推荐阅读