- 浏览: 67281 次
- 性别:
- 来自: 北京
最近访客 更多访客>>
最新评论
-
sunwang810812:
这两个方法都对,只不过我个人觉得,泛型方法的主要目的是为了 p ...
java泛型方法的使用 -
a596620989:
第二个方法是不能通过编译的,会报T can't be reso ...
java泛型方法的使用 -
Javakeith:
...
java泛型 -
bannamoon:
两个方法当然有区别了。当你需要应用泛型规则的时候。第二个方法就 ...
java泛型方法的使用 -
luoping0425:
Learning English!
PEACE—宁静
出自《java puzzle》
下面的程序使用定制的比较器,对一个由随机挑选的Integer实例组成的数组进行排序,然后打印了一个描述了数组顺序的单词。回忆一下,Comparator接口只有一个方法,即compare,它在第一个参数小于第二个参数时返回一个负数,在两个参数相等时返回0,在第一个参数大于第二个参数时返回一个整数。这个程序是展示5.0版特性的一个样例程序。它使用了自动包装和解包、泛型和枚举类型。那么,它会打印出什么呢?
该程序的main方法创建了一个Integer实例的数组,并用随机数对其进行了初始化,然后用比较器cmp对该数组进行排序。这个比较器的compare方法将返回它的第二个参数减去第一个参数的值,如果第二个参数表示的是比第一个参数大的数值,其返回值就是正的;如果这两个参数相等,其返回值为0;如果第二个参数表示的是比第一个参数小的数值,其返回值就是负的。这种行为正好与compare方法通常的做法相反,因此,该比较器应该施加的是降序排列。
在对数组排序之后,main方法将该数组传递给了静态方法order,然后打印由这个方法返回的结果。该方法在数组中所有的元素都表示相同的数值时,返回CONSTANT;在数组中每一对毗邻的元素中第二个元素都大于等于第一个元素时,返回ASCENDING;在数组中每一对毗邻的元素中第二个元素都小于等于第一个元素时,返回DESCENDING;在这些条件都不满足时,返回UNORDERED。尽管理论上说,数组中的100个随机数有可能彼此都相等,但是这种奇特现象发生的非常小:232×99分之一,即大约5×10953分之一。因此,该程序看起来应该打印DESCENDING。如果你运行该程序,几乎可以肯定你将看到它打印的是UNORDERED。为什么它会产生如此的行为呢?
order方法很直观,它并不会说谎。Arrays.sort方法已经存在许多年了,它工作得非常好。现在只有一个地方能够发现bug了:比较器。乍一看,这个比较器似乎不可能出错。毕竟,它使用的是标准的惯用法:如果你有两个数字,你想得到一个数值,其符号表示它们的顺序,那么你可以计算它们的差。这个惯用法至少从1970年代早期就一直存在了,它在早期的UNIX里面被广泛地应用。遗憾的是,这种惯用法从来都没有正确地工作过。本谜题也许应该称为“白痴一般的惯用法的案例”。这种惯用法的问题在于定长的整数没有大到可以保存任意两个同等长度的整数之差的程度。当你在做两个int或long数值的减法时,其结果可能会溢出,在这种情况下我们就会得到错误的符号。
例如,请考虑下面的程序:
很明显,x比z小,但是程序打印的是294967296,它是一个正数。既然这种比较的惯用法是有问题的,那么为什么它会被如此广泛地应用呢?因为它在大多数时间里可以正常工作的。它只在用来来进行比较的两个数字的差大于Integer.MAX_VALUE的时候才会出问题。这意味着对于许多应用而言,在实际使用中是不会看到这种错误的。更糟的是,它们被观察到的次数少之又少,以至于这个bug永远都不会被发现和订正。
那么这对于我们的程序的行为意味着什么呢?如果你查阅一下Comparator的文档,你就会看到它所实现的排序关系必须是可传递的(transitive),换句话说,(compare(x,y) > 0)&&(compare(y,z) > 0)蕴含着compare(x,z) > 0。如果我们取Overflow例子中的x和z,并取y为0,那么我们的比较器在这些数值上就违反了可传递性。事实上,在所有随机选取的int数值对中,有四分之一该比较器都会返回错误的值。用这样的比较器来执行一个搜索或排序,或者用它去排序一个有序的集合,都会产生不确定的行为,就像我们在运行本谜题的程序时所看到的那样。出于数学上的倾向性,Comparator.compare方法的一般约定要求比较器要产生一个全序(total order),但是这个比较器在数个计算上都未能做到这一点。
我们可以通过替换遵守上述一般约定的Comparator实现来订正我们的程序。因为我们只是想要反转自然排序的顺序,所以我们甚至可以不必编写我们自己的比较器。Collection类提供了一个可以产生这种顺序的比较器。如果你用Arrays.sort(arr,Collections.reverseOrder())来替代最初的Arrays.sort调用,该程序就可以打印出我们所期望的DESCENDING。
或者,你可以编写你自己的比较器。下面的代码并不“聪明”,但是它可以工作,从而使该程序可以打印出我们所期望的DESCENDING:
public int compare(Integer i1, Integer i2) {
return (i2 < i1 ? -1 : (i2 == i1 ? 0 :1));
}
本谜题有数个教训,最具体的是:不要使用基于减法的比较器,除非你能够确保要比较的数值之间的差永远不会大于Integer.MAX_VALUE [EJ Item 11]。更一般地讲,要意识到int的溢出。另一个教训是你应该避免“聪明”的代码。应该努力去编写清晰正确的代码,不要对它作任何优化,除非该优化被证明是必需的[EJ Item 37]。
下面的程序使用定制的比较器,对一个由随机挑选的Integer实例组成的数组进行排序,然后打印了一个描述了数组顺序的单词。回忆一下,Comparator接口只有一个方法,即compare,它在第一个参数小于第二个参数时返回一个负数,在两个参数相等时返回0,在第一个参数大于第二个参数时返回一个整数。这个程序是展示5.0版特性的一个样例程序。它使用了自动包装和解包、泛型和枚举类型。那么,它会打印出什么呢?
import java.util.*; public class SuspiciousSort { public static void main(String[ ] args) { Random rnd = new Random(); Integer[ ] arr = new Integer[100]; for (int i = 0; i < arr.length; i++) arr[i] = rnd.nextInt(); Comparator<Integer> cmp = new Comparator<Integer>() { public int compare(Integer i1, Integer i2) { return i2 - i1; } }; Arrays.sort(arr, cmp); System.out.println(order(arr)); } enum Order { ASCENDING, DESCENDING, CONSTANT, UNORDERED }; static Order order(Integer[ ] a) { boolean ascending = false; boolean descending = false; for (int i = 1; i < a.length; i++) { ascending |= a[i] > a[i-1]; descending |= a[i] < a[i-1]; } if (ascending && !descending) return Order.ASCENDING; if (descending && !ascending) return Order.DESCENDING; if (!ascending) return Order.CONSTANT; // All elements equal return Order.UNORDERED; // Array is not sorted } }
该程序的main方法创建了一个Integer实例的数组,并用随机数对其进行了初始化,然后用比较器cmp对该数组进行排序。这个比较器的compare方法将返回它的第二个参数减去第一个参数的值,如果第二个参数表示的是比第一个参数大的数值,其返回值就是正的;如果这两个参数相等,其返回值为0;如果第二个参数表示的是比第一个参数小的数值,其返回值就是负的。这种行为正好与compare方法通常的做法相反,因此,该比较器应该施加的是降序排列。
在对数组排序之后,main方法将该数组传递给了静态方法order,然后打印由这个方法返回的结果。该方法在数组中所有的元素都表示相同的数值时,返回CONSTANT;在数组中每一对毗邻的元素中第二个元素都大于等于第一个元素时,返回ASCENDING;在数组中每一对毗邻的元素中第二个元素都小于等于第一个元素时,返回DESCENDING;在这些条件都不满足时,返回UNORDERED。尽管理论上说,数组中的100个随机数有可能彼此都相等,但是这种奇特现象发生的非常小:232×99分之一,即大约5×10953分之一。因此,该程序看起来应该打印DESCENDING。如果你运行该程序,几乎可以肯定你将看到它打印的是UNORDERED。为什么它会产生如此的行为呢?
order方法很直观,它并不会说谎。Arrays.sort方法已经存在许多年了,它工作得非常好。现在只有一个地方能够发现bug了:比较器。乍一看,这个比较器似乎不可能出错。毕竟,它使用的是标准的惯用法:如果你有两个数字,你想得到一个数值,其符号表示它们的顺序,那么你可以计算它们的差。这个惯用法至少从1970年代早期就一直存在了,它在早期的UNIX里面被广泛地应用。遗憾的是,这种惯用法从来都没有正确地工作过。本谜题也许应该称为“白痴一般的惯用法的案例”。这种惯用法的问题在于定长的整数没有大到可以保存任意两个同等长度的整数之差的程度。当你在做两个int或long数值的减法时,其结果可能会溢出,在这种情况下我们就会得到错误的符号。
例如,请考虑下面的程序:
public class Overflow { public static void main(String[] args){ int x = -2000000000; int z = 2000000000; System.out.println(x - z); } }
很明显,x比z小,但是程序打印的是294967296,它是一个正数。既然这种比较的惯用法是有问题的,那么为什么它会被如此广泛地应用呢?因为它在大多数时间里可以正常工作的。它只在用来来进行比较的两个数字的差大于Integer.MAX_VALUE的时候才会出问题。这意味着对于许多应用而言,在实际使用中是不会看到这种错误的。更糟的是,它们被观察到的次数少之又少,以至于这个bug永远都不会被发现和订正。
那么这对于我们的程序的行为意味着什么呢?如果你查阅一下Comparator的文档,你就会看到它所实现的排序关系必须是可传递的(transitive),换句话说,(compare(x,y) > 0)&&(compare(y,z) > 0)蕴含着compare(x,z) > 0。如果我们取Overflow例子中的x和z,并取y为0,那么我们的比较器在这些数值上就违反了可传递性。事实上,在所有随机选取的int数值对中,有四分之一该比较器都会返回错误的值。用这样的比较器来执行一个搜索或排序,或者用它去排序一个有序的集合,都会产生不确定的行为,就像我们在运行本谜题的程序时所看到的那样。出于数学上的倾向性,Comparator.compare方法的一般约定要求比较器要产生一个全序(total order),但是这个比较器在数个计算上都未能做到这一点。
我们可以通过替换遵守上述一般约定的Comparator实现来订正我们的程序。因为我们只是想要反转自然排序的顺序,所以我们甚至可以不必编写我们自己的比较器。Collection类提供了一个可以产生这种顺序的比较器。如果你用Arrays.sort(arr,Collections.reverseOrder())来替代最初的Arrays.sort调用,该程序就可以打印出我们所期望的DESCENDING。
或者,你可以编写你自己的比较器。下面的代码并不“聪明”,但是它可以工作,从而使该程序可以打印出我们所期望的DESCENDING:
public int compare(Integer i1, Integer i2) {
return (i2 < i1 ? -1 : (i2 == i1 ? 0 :1));
}
本谜题有数个教训,最具体的是:不要使用基于减法的比较器,除非你能够确保要比较的数值之间的差永远不会大于Integer.MAX_VALUE [EJ Item 11]。更一般地讲,要意识到int的溢出。另一个教训是你应该避免“聪明”的代码。应该努力去编写清晰正确的代码,不要对它作任何优化,除非该优化被证明是必需的[EJ Item 37]。
发表评论
-
有毒的括号垃圾
2009-05-16 17:01 1071出自《java puzzle》 你能否举出这样一个合法的J ... -
诵读困难者的一神论
2009-05-08 15:17 1079出自《java puzzle》 从前有一个人,他认为世上只 ... -
烧焦到无法识别
2009-05-06 00:01 1014出自《java puzzle》 下面 ... -
反射的污染
2009-04-30 14:25 2452出自《java puzzle》 这个谜题举例说明了一个关于 ... -
异常为循环而抛
2009-04-22 15:33 1028出自《java puzzle》 下面的程序循环遍历了一个i ... -
不情愿的构造器
2009-04-13 23:38 1012出自《java puzzle》 尽管 ... -
不受欢迎的宾客
2009-04-10 13:16 826出自《java puzzle》 本谜题中的程序所建模的系统, ... -
极端不可思议
2009-04-09 13:38 854出自《java puzzle》 本 ... -
同一性的危机
2009-04-08 13:46 732出自《java puzzle》 下面的程序是不完整的,它缺 ... -
你的隐私正在被公开
2009-04-07 22:34 716出自《java puzzle》 私 ... -
终极危难
2009-04-03 11:48 751出自《java puzzle》 本谜题旨在检验当你试图隐藏一 ... -
进口税
2009-04-02 11:08 772出自《java puzzle》 在5.0 ... -
一揽子交易
2009-04-01 10:25 996出自《java puzzle》 下面这个程序设计在不同的包 ... -
灰色的阴影
2009-03-24 11:33 982出自《java puzzle》 下面 ... -
对字符串上瘾
2009-03-23 10:38 781出自 《java puzzle》 一个名字可以被用来引用位于 ... -
一件私事
2009-03-21 13:24 676出自 《java puzzle》 在下面的程序中,子类的一 ... -
名字游戏
2009-03-18 15:35 908出自《java puzzle》 下面 ... -
一行的方法
2009-03-14 16:27 866出自《java puzzle》 现 ... -
差是几个
2009-03-12 13:21 724出自《java puzzle》 下面的程序在计算一个int数 ... -
产生它的散列码
2009-03-11 09:35 729出自 《java puzzle》 本 ...
相关推荐
一种用于防爆储运罐的疑似爆炸物支撑托盘的制作方法.docx
本文介绍了一例利用5G远程机器人超声技术诊断疑似新型冠状病毒感染性肺炎的病例,揭示了远程医疗、人工智能和医学影像诊断领域的新进展。报告详细描述了一位老年患者的病情、诊疗过程和最终结果,体现了5G技术在医疗...
对于肺癌而言,CT扫描是一种有效的筛查和诊断手段,但随之而来的大量图像数据给诊断工作带来了巨大挑战。因此,提高图像中疑似病灶检测的准确性,减少医生的漏诊和误诊几率,是当前医学图像处理研究的热点之一。 ...
研究通过回顾性分析2020年1月至3月期间郑州大学第一附属医院收治的206例疑似病例的胸部CT影像和临床数据。确诊患者包括轻型、普通型、重型和危重型患者,而排除的则包括其他类型肺炎和胸部CT未见明显异常者。 研究...
在医疗信息化领域,疑似传染病人转诊记录表是医疗机构中非常关键的一环,主要用于管理和跟踪可能患有传染病的患者。此文档的主要目的是确保在病患转诊过程中,信息的准确无误,以便接收医院能及时采取适当的隔离和...
疑似传染病病人情况登记表75703.doc
一种基于深度学习的层次化钓鱼网站检测方法 深度学习在近些年来获得了快速发展,已经广泛应用于图像识别、自然语言处理、语音识别等领域。然而,在网络安全领域,深度学习的应用仍在不断扩展,例如钓鱼网站检测就是...
总结来说,这篇论文探讨了如何运用社交网络数据,尤其是大学生微博,开发出一种初步筛选疑似抑郁内容的算法。通过抑郁关键词的扩展和语义分析,该算法能有效地从大规模数据中找出可能反映抑郁情绪的微博,从而为抑郁...
基于混合高斯模型,提出一种双重阴影消除策略,首先通过HSV模型下的颜色夹角确定疑似阴影,再对运动目标和疑似阴影进行混合高斯建模而消除实际阴影。通过实验表明,该策略在不影响目标识别的情况下可以较好的检测并...
为了提高网络安全性能,有研究者提出了一种基于模糊网络阈值计算的入侵疑似边界确定算法。这种方法通过计算模糊网络阈值,来确定云计算环境下入侵检测中的疑似边界具体参数。 模糊网络阈值计算法是基于模糊逻辑的...
综上所述,本文提出了一种基于帧差法的夜间车辆检测方法,通过有效抑制光晕影响、引入“疑似车辆”的概念以及根据运动目标分布情况判断车型等手段,大大提高了夜间车辆检测的准确性。该方法不仅能够适应复杂的夜间...
本文介绍了一种适合在FPGA(现场可编程门阵列)上实现的中值滤波改进算法,并通过软硬件协同仿真验证了算法的处理效果。中值滤波是一种图像处理技术,用于去除图像噪声,特别是在去除脉冲干扰和图像扫描噪声方面效果...
疲劳驾驶是全球交通安全的重大隐患之一,据统计,美国每年因疲劳驾驶而发生的交通事故数量惊人。鉴于眼睛的状态能够反映驾驶员的疲劳程度,因此,准确快速地定位人眼对于判断驾驶员的疲劳状态至关重要。 #### 方法...
本文提出了一种在目标筛选法基础上利用目标的外接正矩形获取相应的参数,并依据这些参数进行目标几何特性分析、剔除伪目标、保留真目标及疑似目标,直到最终识别目标的方法。 目标识别是图像处理的高级阶段,快速、...
对边界法向量叠加法进行改进,提出一种基于局部形状约束和自适应设定法向量大小的边界法向量叠加疑似肺结节提取方法。首先用自适应阈值方法对肺实质图像进行分割,得到初始 ROI区域 ;然后判断初始 ROI区域边界点的局部...
骨肽注射液是一种广泛用于骨疾病治疗的药物,但国家药品不良反应监测中心的数据表明,该药物可能导致严重的过敏反应。这不仅增加了患者的住院时间,加重了他们的经济负担,甚至可能威胁到患者的生命安全。针对这一...