【问题标题】:Java reverse order of sublist given wrong results给出错误结果的子列表的Java反向顺序
【发布时间】:2021-02-04 21:53:11
【问题描述】:

我遇到了问题。我将代码简化为一个小型演示程序。有 1 个名为 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&lt;Visitor&gt; 并为其添加 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 个 foundVisitor 进行反向排序。为此,我将代码注释掉了,但我有以下几行:

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

这里给出的代码是我的整个项目,所以你们都可以复制它:)
出了什么问题,我该如何解决?

【问题讨论】:

    标签: java sorting arraylist collections


    【解决方案1】:

    要解决您的问题,您只需执行以下操作,替换:

    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 方法不会返回新的list,而是返回原始列表的视图。正如人们可以阅读here

    subList(int fromIndex, int toIndex) 返回部分的视图 此列表介于指定的 fromIndex 和 toIndex 之间, 独家。

    当你这样做时

    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推荐:

    使用基于索引的循环将消除执行二进制的需要 搜索您已经知道其位置的元素。其实你 可以从这个索引向后循环并打印五个元素,然后 您既不需要子列表也不需要反向排序。

    所以你的代码可以简化为:

    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();
        }
    }
    

    【讨论】:

    • 谢谢!你的解释很清楚:)
    【解决方案2】:

    排序很好。我忽略了你的循环,只是这样做了。

     visitorList.sort(Comparator.comparing(Visitor::getId));
     visitorList.forEach(System.out::println);
     visitorList.sort(Comparator.comparing(Visitor::getId).reversed());
     visitorList.forEach(System.out::println);
    

    它们按预期排序和打印。

    我建议你把它放在你的访问者类中,这样你就可以打印对象了。

    @Override
    public String toString() {
        return name + " " + id;
    }
    

    不要修改您的sublist。否则你会改变原来的内容。如果你对他sublist 进行排序,它只会对这些项目进行排序。那么当你打印原始列表的项目时,只有sublist部分会被排序。

    【讨论】:

      【解决方案3】:

      这里的问题在于您使用方法 subList() 的方式,特别是以下语句:

      List&lt;Visitor&gt; foundVisitors = visitorList.subList(startIndex, endIndex);

      根据Java规范对于List的方法subList:

      返回的列表是由这个列表支持的,所以返回列表中的非结构性变化会反映在这个列表中,反之亦然。

      这意味着当你在子列表“foundVisitors”中应用“排序”时,它会立即反映在主列表“visitorList”上,最终结果将是一团糟。

      要解决这个问题,您必须从子列表中创建一个新列表,例如,您可以将您的行替换为以下行:

         List<Visitor> foundVisitors = new ArrayList<>(visitorList.subList(startIndex, endIndex));
      
         // REVERSE THE SORTING
         Collections.sort(foundVisitors, Collections.reverseOrder());
      

      试试这个,看看它是如何工作的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-13
        • 1970-01-01
        • 2020-05-27
        • 2011-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多