Java 中如何根据另一个按位置配对的集合对 LinkedHashSet 元素排序?
数据阿明
可以按照两个 LinkedHashSet 的迭代顺序逐项配对,将配对结果按 pitch 升序排列,再取出对应的 slotID。示例数据的结果是 [3, 4, 5, 2, 1]。
确认去重后的配对关系
LinkedHashSet 按插入顺序迭代。重复添加已有元素不会增加新元素,也不会改变该元素的位置。原代码执行完毕后,两个集合的内容和迭代顺序如下:
slotIDs = [3, 1, 2, 5, 4]
pitches = [0.5, 2.2, 1.3, 1.1, 0.7]
按相同位置配对后,各元素的升序位置如下:
| slotID | 配对的 pitch | 升序位置 |
|---|---|---|
| 3 | 0.5f | 1 |
| 1 | 2.2f | 5 |
| 2 | 1.3f | 4 |
| 5 | 1.1f | 3 |
| 4 | 0.7f | 2 |
将配对保存后再排序
下面的实现适用于 Java 8 及以上版本,假设集合和元素均非 null。它返回一个新的 List<Integer>,不会改变原来两个集合的顺序。
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
public final class SlotSorter {
public static List<Integer> sortByPitch(
LinkedHashSet<Integer> slotIDs,
LinkedHashSet<Float> pitches) {
if (slotIDs.size() != pitches.size()) {
throw new IllegalArgumentException(
"slotIDs and pitches must have the same size");
}
List<Map.Entry<Integer, Float>> pairs =
new ArrayList<>(slotIDs.size());
Iterator<Integer> idIterator = slotIDs.iterator();
Iterator<Float> pitchIterator = pitches.iterator();
while (idIterator.hasNext()) {
pairs.add(new AbstractMap.SimpleImmutableEntry<>(
idIterator.next(), pitchIterator.next()));
}
pairs.sort((a, b) ->
Float.compare(a.getValue(), b.getValue()));
List<Integer> result = new ArrayList<>(pairs.size());
for (Map.Entry<Integer, Float> pair : pairs) {
result.add(pair.getKey());
}
return result;
}
}
在原问题的添加代码之后调用:
List<Integer> sortedSlotIDs = SlotSorter.sortByPitch(slotIDs, pitches);
System.out.println(sortedSlotIDs);
// [3, 4, 5, 2, 1]
比较 Float 值时使用 Float.compare。不要将两个值相减后强制转换为 int,这样会丢失小数差异,导致排序错误。该实现的时间复杂度为 O(n log n),额外空间复杂度为 O(n)。
如果结果仍需使用 LinkedHashSet
可以根据排序结果构造一个新的集合,它会按传入列表的顺序保存元素:
LinkedHashSet<Integer> sortedSlotIDs = new LinkedHashSet<>(
SlotSorter.sortByPitch(slotIDs, pitches));
这个顺序来自排序后的列表,LinkedHashSet 本身不会根据 pitch 自动维护排序。
注意独立去重和位置配对的限制
两个集合长度相同是按位置配对的必要条件,但不能证明配对符合业务需求。两个集合会分别去重,如果其中一个集合比另一个少保留了一个元素,后续位置关系就可能发生偏移。使用这个方案的前提是:你确实要将两个集合最终保留的元素按位置配对。
配对完成后,建议将配对列表作为后续处理的数据来源。如果只重排 slotIDs,再将它与原顺序的 pitches 按位置配对,对应关系就会改变。
备注:内容仅供参考。
