首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用日期强制TimSort IllegalArgumentException

使用日期强制TimSort IllegalArgumentException
EN

Stack Overflow用户
提问于 2018-01-19 08:03:47
回答 2查看 233关注 0票数 3

使用Java8 u151

我们正在进行以下排序:

代码语言:javascript
复制
Collections.sort(srs, (sr1, sr2) -> sr1.getValidStartTime().compareTo(sr2.getValidEndTime()));

其中getValidStartTime和getValidEndTime是日期对象。

显然,这段代码是错误的,因为我们的目标实际上是根据开始时间进行排序。

然而,不幸的是,导致问题的数据被从数据库中删除了,我们得到了臭名昭著的IllegalArgumentException。这似乎合乎逻辑,因为在元素之间,我们使用不同的值进行比较(将A.start与B.end进行比较,但将B.start与C.end进行比较)。

我不能做的是找出一个导致这个异常再次抛出的数据集。修复代码以始终比较开始时间是正确的方法,但关于在数据集之前和之后的测试,我无法显示修复的证据(即使每个人都已经同意我们正在接受更改)。

我试过研究抛出这个问题的TimSort的mergeHi方法,但我不能完全理解所有的gallop和Array copy的内容。所以,虽然我知道异常是在哪里抛出的,但让它可重现却遇到了失败。

并且Date对象在排序之间是静态的。一旦在数据库中设置,它们就是不可变的。在排序过程中,列表本身也没有变化。所以对我来说,我可能是错的,这指向了数据,我们有一些奇怪的开始和结束日期的组合,这违反了传递子句,但我尝试过的每个组合总是得到一个有效的(如果看起来不是有点奇怪的话)排序。

谢谢!

EN

回答 2

Stack Overflow用户

发布于 2018-01-19 10:59:17

要做到这一点,最简单的方法就是不断生成随机列表,并尝试排序,直到检查失败。在jdk 1.8.0_111上,导致异常的最短列表是32个元素。以下是可用于初始化Date值并将导致异常的开始/结束时间戳列表:

代码语言:javascript
复制
{start=68208554877356084, end=3791095193142800835}
{start=248264922016936970, end=5326356389367348592}
{start=70847878331153962, end=1329864610265504554}
{start=299053597297857298, end=4543460986676142955}
{start=2075045414748723202, end=1193808915252175698}
{start=1888180037471781608, end=2492314749794483810}
{start=506596727265987351, end=2390472400080050280}
{start=4533260585328085001, end=2273691205607504663}
{start=5678209310012100575, end=959412777775545678}
{start=2732174389724934002, end=1780458709387750881}
{start=3098641550091084357, end=7078749384785410602}
{start=556524021068368297, end=8482788837298542192}
{start=98318333071465581, end=4156661237757928788}
{start=2084735587245502205, end=4379712643293008540}
{start=3165092267008534695, end=3427927233210778860}
{start=3109680552226050258, end=7303065366486904947}
{start=4928610946211198422, end=6426677832945805822}
{start=965369716172656147, end=6219167484686793206}
{start=805041445200191777, end=2942634988195806902}
{start=8045405997433808237, end=6001857015663585724}
{start=6633159983148701791, end=1448351075620872268}
{start=4539362557763873114, end=8432020244491782408}
{start=7435017849572867526, end=5951614001812150640}
{start=9205367993832979048, end=1341233048581746570}
{start=8478026453445310753, end=530855741009189818}
{start=4638397659065784972, end=2597599860193612976}
{start=3683809635722753669, end=8506390941316155399}
{start=5946468237919207244, end=3711093891423756040}
{start=6965128507257577261, end=8627460098134987362}
{start=7493578845247407113, end=8568660839840900159}
{start=7097494652946649557, end=8999069292652823540}
{start=9190087421488513073, end=20737341215892578}
票数 5
EN

Stack Overflow用户

发布于 2018-01-20 04:13:45

很难找出会导致TimSort抛出“比较方法违反其一般约定!”的数据!当给出一个有错误的比较器时,会引发异常。作为Misha pointed out (+1),输入的长度必须至少为32个元素,并且生成随机数据直到得到导致异常的数据似乎是一种合理的方法。

拥有这样的输入数据可能看起来很有用,可以表明bug实际上已经修复了。毕竟,它用旧的比较器崩溃了,大概不会用固定的比较器崩溃。这是一个证据,但它实际上并没有显示固定的比较器实际上是固定的;它可能不会对此输入数据造成问题,但它也可能会与其他输入数据崩溃。

问题是,你想在这方面走多远?

依靠图书馆

一种方法是简单地使用Java8构造来创建比较器。您可以重写语句,如下所示:

代码语言:javascript
复制
Collections.sort(srs, Comparator.comparing(SR::getValidStartTime));

如果您认为库的排序例程有效,并且Comparator.comparing例程有效,那么您可能不需要对此进行测试。

测试比较器合同要求

如果您真的想测试这个比较器(或者您有一个更复杂的比较器需要测试),一种不同的方法是根据Comparator.compare方法规范中列出的要求为它编写一些单元测试:

反对称:对于所有x和y,sgn(compare(x, y)) == -sgn(compare(y, x))

  • Transitivity:((compare(x, y)>0) && (compare(y, z)>0))暗示compare(x, z)>0 sgn(compare(x, y)) == -sgn(compare(y, x))

  • Transitivity:

compare(x, z)>0z compare(x, y)==0暗示sgn(compare(x, z))==sgn(compare(y, z)) compare(x, z)>0 z

您可以编写一些单元测试,这些测试接受一组固定的值,并验证给定的比较器是否具有这些属性,以获得来自该值集的所有输入组合。

验证排序结果的排序

还有一种方法是获取一些输入数据(真实的或随机生成的),并使用比较器对其进行排序。如果它没有崩溃,请验证结果列表是否为该比较器强加的顺序。如果比较器的行为不一致,则排序项的结果可能甚至很可能不会产生一个根据该比较器实际排序的列表。这是因为在排序过程中应用比较器的项与在比较相邻项时应用比较器的项不同。当然,这并不能保证能发现错误,但这是一个潜在的有用的交叉检查。请注意,这不会测试比较器在语义上是否正确。它仅仅是对比较器行为是否一致的一个测试。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48332261

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档