Java 给出错误结果的子列表的逆序
Java reverse order of sublist given wrong results
我有问题。我将我的代码简化为一个小的演示程序。有 1 个 class 名为 Visitor
,它看起来像这样:
public class Visitor implements Comparable<Visitor> {
private Integer id;
private String name;
private static int lastIdGiven = 0;
public Visitor(Integer id) {
this.id = id;
}
public Visitor(String name) {
this.id = getNewId();
this.name = name;
}
private int getNewId() {
this.lastIdGiven++;
return this.lastIdGiven;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Override
public int compareTo(Visitor o) {
return this.id.compareTo(o.id);
}
}
然后我创建一个 List<Visitor>
并为我的示例添加 20 个访问者。我想要的是遍历列表并根据 Id 抓取当前访问者下方的 5 位访问者。
这是相关代码:
public class Main {
public static final int NUM_OF_VISITORS_IN_LIST = 5;
public static void main(String[] args) {
ArrayList<Visitor> visitorList = new ArrayList<>();
for (Integer i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i.toString());
visitorList.add(visitor);
}
visitorList.sort(Visitor::compareTo);
for(Visitor visitor : visitorList) {
int visitorSearchId = visitor.getId();
Visitor searchVisitor = new Visitor(visitorSearchId);
int endIndex = Collections.binarySearch(visitorList, searchVisitor);
int startIndex = endIndex - NUM_OF_VISITORS_IN_LIST;
if (startIndex >= 0) {
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
// REVERSE THE SORTING
//foundVisitors.sort(Visitor::compareTo);
//foundVisitors.sort(Collections.reverseOrder());
System.out.println("For visitor-Id: " + visitor.getId() + ", are the following visitors found:");
for (Visitor foundVisitor : foundVisitors) {
System.out.println("\tId: " + foundVisitor.getId());
}
System.out.println();
}
}
}
}
这是一位访客打印的:
For visitor-Id: 19, are the following visitors found:
Id: 14
Id: 15
Id: 16
Id: 17
Id: 18
但现在我想对找到的 5 个访问者进行反向排序。为此,我注释掉了代码,但我有这些行:
foundVisitors.sort(Visitor::compareTo);
foundVisitors.sort(Collections.reverseOrder());
然而,这导致我得到一个非常奇怪的列表,它从原始列表中获取旧数据:
For visitor-Id: 19, are the following visitors found:
Id: 18
Id: 4
Id: 3
Id: 2
Id: 1
我以为我会得到以下输出:
For visitor-Id: 19, are the following visitors found:
Id: 18
Id: 17
Id: 16
Id: 15
Id: 14
这里给出的代码,是我的整个项目,大家可以重现:)
出了什么问题,我该如何解决?
要解决您的问题,您只需执行以下操作,替换:
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
foundVisitors.sort(Collections.reverseOrder());
用于:
List<Visitor> sublist = visitorList.subList(startIndex, endIndex);
List<Visitor> foundVisitors = new ArrayList<>(sublist);
foundVisitors.sort(Collections.reverseOrder());
subList
方法不会return 新的 list
而是原始列表的视图。可以读到 here:
subList(int fromIndex, int toIndex) Returns a view of the portion of
this list between the specified fromIndex, inclusive, and toIndex,
exclusive.
因此当你这样做时
foundVisitors.sort(Collections.reverseOrder());
您已经弄乱了原始列表的顺序。因此,这就是为什么你需要先做
List<Visitor> foundVisitors = new ArrayList<>(sublist);
边注:
for (Integer i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i.toString());
visitorList.add(visitor);
}
可以简化为:
for (int i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i);
visitorList.add(visitor);
}
更新:
与你的问题的主要问题无关,但仍然是一个值得一提的优化。 Hoger 推荐:
Using an index based loop would eliminate the need to do a binary
search for the element whose position you already know. Actually, you
could loop from this index backwards and print the five elements, then
you would need neither, sub list nor reverse sorting.
所以你的代码可以简化为:
public static void main(String[] args) {
ArrayList<Visitor> visitorList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i);
visitorList.add(visitor);
}
visitorList.sort(Visitor::compareTo);
for(int i = NUM_OF_VISITORS_IN_LIST; i < visitorList.size(); i++) {
System.out.println("For visitor: " + visitorList.get(i));
for(int j = i, end = i - NUM_OF_VISITORS_IN_LIST; j > end;) {
System.out.println(visitorList.get(--j).getId());
}
System.out.println();
}
}
排序很好。我忽略了你的循环,只是这样做了。
visitorList.sort(Comparator.comparing(Visitor::getId));
visitorList.forEach(System.out::println);
visitorList.sort(Comparator.comparing(Visitor::getId).reversed());
visitorList.forEach(System.out::println);
他们按预期排序和打印。
我建议你把这个放在你的访客中 class 这样你就可以打印对象了。
@Override
public String toString() {
return name + " " + id;
}
并且不要修改您的 sublist
。否则你将改变原来的内容。如果你对 he sublist
进行排序,它只会对这些项目进行排序。然后当你打印原始列表的项目时,只会对 sublist
部分进行排序。
这里的问题在于您使用方法 subList() 的方式,特别是这条语句:
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
根据 List 的方法 subList 的 Java 规范:
The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
这意味着当你在子列表“foundVisitors”中应用“排序”时,它会立即反映在主列表“visitorList”上,最后的结果将是一团糟。
要解决这个问题,您必须从子列表创建一个新列表,例如您可以用以下行替换您的行:
List<Visitor> foundVisitors = new ArrayList<>(visitorList.subList(startIndex, endIndex));
// REVERSE THE SORTING
Collections.sort(foundVisitors, Collections.reverseOrder());
试试看效果如何。
我有问题。我将我的代码简化为一个小的演示程序。有 1 个 class 名为 Visitor
,它看起来像这样:
public class Visitor implements Comparable<Visitor> {
private Integer id;
private String name;
private static int lastIdGiven = 0;
public Visitor(Integer id) {
this.id = id;
}
public Visitor(String name) {
this.id = getNewId();
this.name = name;
}
private int getNewId() {
this.lastIdGiven++;
return this.lastIdGiven;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
@Override
public int compareTo(Visitor o) {
return this.id.compareTo(o.id);
}
}
然后我创建一个 List<Visitor>
并为我的示例添加 20 个访问者。我想要的是遍历列表并根据 Id 抓取当前访问者下方的 5 位访问者。
这是相关代码:
public class Main {
public static final int NUM_OF_VISITORS_IN_LIST = 5;
public static void main(String[] args) {
ArrayList<Visitor> visitorList = new ArrayList<>();
for (Integer i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i.toString());
visitorList.add(visitor);
}
visitorList.sort(Visitor::compareTo);
for(Visitor visitor : visitorList) {
int visitorSearchId = visitor.getId();
Visitor searchVisitor = new Visitor(visitorSearchId);
int endIndex = Collections.binarySearch(visitorList, searchVisitor);
int startIndex = endIndex - NUM_OF_VISITORS_IN_LIST;
if (startIndex >= 0) {
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
// REVERSE THE SORTING
//foundVisitors.sort(Visitor::compareTo);
//foundVisitors.sort(Collections.reverseOrder());
System.out.println("For visitor-Id: " + visitor.getId() + ", are the following visitors found:");
for (Visitor foundVisitor : foundVisitors) {
System.out.println("\tId: " + foundVisitor.getId());
}
System.out.println();
}
}
}
}
这是一位访客打印的:
For visitor-Id: 19, are the following visitors found:
Id: 14
Id: 15
Id: 16
Id: 17
Id: 18
但现在我想对找到的 5 个访问者进行反向排序。为此,我注释掉了代码,但我有这些行:
foundVisitors.sort(Visitor::compareTo);
foundVisitors.sort(Collections.reverseOrder());
然而,这导致我得到一个非常奇怪的列表,它从原始列表中获取旧数据:
For visitor-Id: 19, are the following visitors found:
Id: 18
Id: 4
Id: 3
Id: 2
Id: 1
我以为我会得到以下输出:
For visitor-Id: 19, are the following visitors found:
Id: 18
Id: 17
Id: 16
Id: 15
Id: 14
这里给出的代码,是我的整个项目,大家可以重现:)
出了什么问题,我该如何解决?
要解决您的问题,您只需执行以下操作,替换:
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
foundVisitors.sort(Collections.reverseOrder());
用于:
List<Visitor> sublist = visitorList.subList(startIndex, endIndex);
List<Visitor> foundVisitors = new ArrayList<>(sublist);
foundVisitors.sort(Collections.reverseOrder());
subList
方法不会return 新的 list
而是原始列表的视图。可以读到 here:
subList(int fromIndex, int toIndex) Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
因此当你这样做时
foundVisitors.sort(Collections.reverseOrder());
您已经弄乱了原始列表的顺序。因此,这就是为什么你需要先做
List<Visitor> foundVisitors = new ArrayList<>(sublist);
边注:
for (Integer i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i.toString());
visitorList.add(visitor);
}
可以简化为:
for (int i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i);
visitorList.add(visitor);
}
更新:
与你的问题的主要问题无关,但仍然是一个值得一提的优化。 Hoger 推荐:
Using an index based loop would eliminate the need to do a binary search for the element whose position you already know. Actually, you could loop from this index backwards and print the five elements, then you would need neither, sub list nor reverse sorting.
所以你的代码可以简化为:
public static void main(String[] args) {
ArrayList<Visitor> visitorList = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Visitor visitor = new Visitor("visitor " + i);
visitorList.add(visitor);
}
visitorList.sort(Visitor::compareTo);
for(int i = NUM_OF_VISITORS_IN_LIST; i < visitorList.size(); i++) {
System.out.println("For visitor: " + visitorList.get(i));
for(int j = i, end = i - NUM_OF_VISITORS_IN_LIST; j > end;) {
System.out.println(visitorList.get(--j).getId());
}
System.out.println();
}
}
排序很好。我忽略了你的循环,只是这样做了。
visitorList.sort(Comparator.comparing(Visitor::getId));
visitorList.forEach(System.out::println);
visitorList.sort(Comparator.comparing(Visitor::getId).reversed());
visitorList.forEach(System.out::println);
他们按预期排序和打印。
我建议你把这个放在你的访客中 class 这样你就可以打印对象了。
@Override
public String toString() {
return name + " " + id;
}
并且不要修改您的 sublist
。否则你将改变原来的内容。如果你对 he sublist
进行排序,它只会对这些项目进行排序。然后当你打印原始列表的项目时,只会对 sublist
部分进行排序。
这里的问题在于您使用方法 subList() 的方式,特别是这条语句:
List<Visitor> foundVisitors = visitorList.subList(startIndex, endIndex);
根据 List 的方法 subList 的 Java 规范:
The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
这意味着当你在子列表“foundVisitors”中应用“排序”时,它会立即反映在主列表“visitorList”上,最后的结果将是一团糟。
要解决这个问题,您必须从子列表创建一个新列表,例如您可以用以下行替换您的行:
List<Visitor> foundVisitors = new ArrayList<>(visitorList.subList(startIndex, endIndex));
// REVERSE THE SORTING
Collections.sort(foundVisitors, Collections.reverseOrder());
试试看效果如何。