使用日期强制TimSort IllegalArgumentException

时间:2018-01-19 00:03:47

标签: java java-8 comparator timsort

使用Java 8 u151

我们正在进行以下排序:

Collections.sort(srs, (sr1, sr2) -> sr1.getValidStartTime().compareTo(sr2.getValidEndTime()));

其中getValidStartTime和getValidEndTime是Date对象。

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

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

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

试图查看TimSort的mergeHi方法,但是我无法完全遵循所有的疾驰和数组副本。因此,虽然我可以告诉异常抛出的位置,但是它的可重现性已经失败了。

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

谢谢!

2 个答案:

答案 0 :(得分:5)

最简单的方法就是继续生成随机列表并尝试排序,直到它检查完毕。在jdk 1.8.0_111上,导致异常的最短列表是32个元素长。以下是可用于初始化Date值并将导致异常的开始/结束时间戳列表:

{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}

答案 1 :(得分:4)

提出导致public class SOOrderEntryExt : PXGraphExtension<SOOrderEntry> { public PXAction<SOOrder> RecalculateDiscountsFromImport; [PXButton] [PXUIField(DisplayName = "Recalculate Discounts", MapEnableRights = PXCacheRights.Select, MapViewRights = PXCacheRights.Select, Visible = false)] public void recalculateDiscountsFromImport() { foreach (SOLine line in Base.Transactions.Select()) { SOLine oldLine = Base.Transactions.Cache.CreateCopy(line) as SOLine; DiscountEngine<SOLine>.RecalculateDiscountsLine<SOOrderDiscountDetail>( Base.Transactions.Cache, Base.Transactions, line, line, Base.DiscountDetails, Base.Document.Current.BranchID, Base.Document.Current.CustomerLocationID, Base.Document.Current.OrderDate.Value); TX.TaxAttribute.Calculate<SOLine.taxCategoryID>( Base.Transactions.Cache, new PXRowUpdatedEventArgs(line, oldLine, false)); } Base.Transactions.Cache.IsDirty = true; } } 抛出“比较方法违反其总合同”的数据相当困难!例外,给出一个有缺陷的比较器。作为Misha pointed out(+ 1),输入必须至少为32个元素,并且生成随机数据,直到得到导致异常的数据,这似乎是一种合理的方法。

拥有这样的输入数据似乎有助于显示错误实际上已修复。毕竟,它与旧的Comparator一起崩溃,并且可能它与固定的Comparator没有崩溃。这是一个证据,但它实际上并没有表明固定的比较器实际上是固定的;它可能不会导致此输入数据出现问题,但它也可能与其他一些输入数据崩溃。

问题是,你想用这个去多远?

依靠图书馆

一种方法是简单地使用Java 8构造来创建Comparator。您可以按如下方式重写语句:

TimSort

如果您认为库的排序例程有效,并且Collections.sort(srs, Comparator.comparing(SR::getValidStartTime)); 例程有效,那么您可能不需要对此进行测试。

测试比较器合同要求

如果你真的想测试这个比较器(或者你有一个更复杂的比较器,你想要测试),另一种方法是根据{{{}中列出的要求为它编写一些单元测试。方法规范:

  • 反对称:适用于所有 x y Comparator.comparing

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

  • 可替代性:compare(x, z)>0表示所有z compare(x, y)==0

对于从值集中获取的所有输入组合,您可以编写一些单元测试,这些单元测试采用一组固定值并验证给定的Comparator是否具有这些属性。

验证排序结果的排序

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