对binarySearch(Java)反复使用for循环对列表进行升序排序

时间:2020-11-08 17:05:29

标签: java for-loop binary-search

我有一个清单,试图通过像这样在for循环中添加项目来按升序排序。

    private static void addObjectToIndex(classObject object) {
        for (int i=0;i<collection.size();i++)
        {
            if (collection.get(i).ID >= object.ID)
            {
                collection.insertElementAt(object, i);
                return;
            }
        }
        if (classObject.size() == 0)
            classObject.add(object);
    }

这比每次调用该函数都要快,因为那样会更简单但更慢,因为它给O(N)时间,而不是每次都使用Collections.sort的O(N log N)(除非我是错误的。)

问题是,当我运行Collections.binarySearch尝试从Vector集合中抓取一个项目(该集合需要原子调用方法)时,它仍然最终返回负数,如下面的代码所示。 / p>

        Comparator<classObject> c = new Comparator<classObject>() 
        { 
            public int compare(classObject u1, classObject u2) 
            {
                int z1 = (int)(u1).ID;
                int z2 = (int)(u2).ID;
                if(z1 > z2)
                    return 1;
                return z2 <= z1 ? 0 : -1;
            }

        }; 
        int result = Collections.binarySearch(collection, new classObject(pID), c);
        if (result < 0)
            return null;
        if (collection.get(result).ID != pID)
            return null;
        else
            return collection.get(result);

类似

result = -1043246

显示在调试器中,导致第二个代码段返回null。

我这里缺少什么吗?可能脑子死了很简单。我尝试调整将事物按顺序放置的for循环,<=,> =,<和>,但它不起作用。将对象添加到索引i + 1无效。仍然返回null,这会使整个程序崩溃。

任何见识都会受到赞赏。

1 个答案:

答案 0 :(得分:1)

男孩,您是80年代到达这里的,因为听起来确实您错过了很多API更新!

这比每次调用该函数都要快,因为那样会更简单但更慢,因为它给O(N)时间,而不是每次都使用Collections.sort的O(N log N)(除非我是错误的。)

您现在要在每个插入内容上花费O(n)投资,因此总计为O(n ^ 2),而模型“先添加要添加的所有内容而不进行排序”,然后“最后,对整个列表进行排序”,即O(n logn)。

Vector是线程安全的,这就是为什么我使用它而不是其他东西,并且不能更改

不。线程安全并不是那么容易。您写的内容不是线程安全的。

向量已过时,切勿使用。 Vector的功能(与ArrayList相比)是,对Vector的每个单独操作都是线程安全的(即原子的)。请注意,如果您确实需要使用List<T> myList = Collections.synchronizedList(someList);,则可以从任何列表中获得此行为,但是您极不可能希望这样做。

以您当前的addObjectToIndex的印象。它不是原子的:它使向量上的很多方法调用不同,并且这些方法对一致性的保证为零。如果两个线程都调用addObjectToIndex,并且您的计算机具有多个内核,那么最终您将得到一个清单,该清单看起来像:[1, 2, 5, 4, 10]-即未排序。

采用您的addObjectToIndex方法:除非该方法在整个运行中对集合的视图一致,否则该方法将无法正常工作。换句话说,该块必须是“原子的”-它要么全部完成,要么什么都不做,并且需要始终保持一致的视图。在整个块上粘贴synchronized。与Vector不同,Vector仅考虑每个单独的原子调用,而别无其他,这在这里不起作用。一般来说,“仅同步”是一种用于多核的效率很低的方法-java.util.concurrent中的各种集合通常效率更高且更易于使用,您应该通读该API并查看是否有任何东西。会为你工作。

if(z1 > z2) return 1;

我很确定您的插入代码按升序排列,但您的比较器按降序排列。这会破坏二进制搜索代码(如果未对列表进行排序,则二进制搜索代码被指定为返回任意垃圾,就您在此使用的比较器而言,则不是)。您应该在相关的任何时候使用相同的比较器,并且不要多次重新实现逻辑(或者,如果这样做,至少要对其进行测试!)。

也不需要编写所有这些代码。

Comparator<classObject> c = Comparator::comparingInt(co -> co.ID);

是您所需要的。

但是

看起来您真正想要的是一个使自身不断排序的集合。 Java有它;它称为TreeSet。您将其传递给Comparator(或者不这样做),TreeSet希望您放入的元素具有自然顺序,无论哪种都可以),它将以非常便宜的价格( far 比您的O(n ^ 2)!更好)。 IS 是一个集合,这意味着如果比较器说2个项目相等,则将两者加到集合结果中会导致第二个add调用被忽略(集合所包含的相同元素不能超过一次,对于TreeSet,“相同元素”仅通过“比较它们返回0”来定义-TreeSet忽略hashCode并完全等于)。

这听起来像您真正想要的。如果仍然需要添加2个具有相同ID的不同对象,则将更多字段添加到比较器中(而不是使用相同ID返回0,而是继续检查插入时间戳)。但是,使用“ ID”之类的名称,听起来就不会重复。

之所以要使用这些现成的东西,是因为否则您需要自己做,并且,如果您要努力自己编写它,则需要成为一名优秀的程序员。您显然还不是(到目前为止-我们所有人都开始了新手学习,后来又学会变得更好,这是事物的自然顺序)。例如,如果我尝试将元素添加到非空集合中,而我尝试添加的元素的ID大于集合中的任何元素,则它只会添加任何内容。那是因为您写了if (classObject.size() == 0) classObject.add(object);,但是您想要classObject.add(object);而没有if。另外,在Java中,我们写的是ClassObject,而不是ClassObject,更一般地说,ClassObject是一个完全没有意义的名称。找到一个更好的名字;这有助于减少代码的混乱度,这个问题确实建议您可以使用其中一些。