亨廷顿
- 亨廷顿谈“后89时代”的中国
简洁、清晰、到位。
亨廷顿的《文明的冲突与世界秩序的重建》,有时间要读一读。
标签: 知性
标签: 知性
标签: 闲暇
邹波的文字总是那么迷人。他最新的《测天》提到的16-18世纪之间耶稣会传教士在中国的作为,真有意思。做了一些笔记。
耶稣会是天主教中的一个教派。500年前,他的教士宣称以"适应"的原则行事。每个会员在获得传教资格之前,都要通过不少于14年的系统训练,不仅是神学,还有各种自然科学知识。然后他们借助以数学为主的技艺,证明自己继承了文艺复兴后期意大利人文主义的精髓,是智慧的主流,处于现代知识的前沿,从而让路德派新教在"智力的较量上不战自败"。
耶稣会在中国最有名的传教士是利玛窦。利玛窦一到中国,立刻发现中国人很愿意接受新的知识,也很温和。他的三个最著名的"弟子":瞿太素学化学,李之藻学习制图,徐光启对几何感兴趣,都是上流知识分子。这三个人说:在利公的帮助下,他们正在"找回中国已经消失的过去,重新测量和计算中国的过去",代表了当时主流知识分子对利玛窦带来的新知识的感受。
培根在《新工具》中说:"认为从未被发现的东西仍然能够用旧的方法来发现,这似乎是荒唐的。"中国人发现,利玛窦带来的新方法能够让他们发现从未发现的东西,令人十分激动。1610年,在科举考试的空隙中,登门寻找利玛窦求教几何的人络绎不绝。有的人下决心,如果中了进士,就立刻丢掉儒家的书本,研究《几何原本》。
利玛窦也发现中国人特别感兴趣的天文学是他的局限,于是他写信给罗马,要求他们派更熟知天文的人来。罗马教廷最后派出以金尼阁和他挑选的精英--共22位博学的传教士--出发前往中国,最后8人到达中国,其中有极具才华的邓玉函,他是伽利略在著名的猞猁学会的同窗。
金尼阁也随身带着7000本书,其中包括哥白尼的《天体运行论》,后来,这些书里相当一部分自然科学的书籍,经过传教士本人的改写和翻译,间接地收进了《四库全书》,成为中国知识分子的合法必读书。
邓玉函带来了他为中国人准备的教科书《远西奇器图说》,其中搜罗了欧洲人使用的各种"新工具",既包括天文仪器,也包括消防水龙。一个叫王徵的陕西官员帮助他将其翻译为中文。王徵这个人以恢复古代的机械为最大乐趣,以至曾耽误了科举,别人每年考一次,他三年才考一次。他相当热心,除了帮助翻译以外,还为了帮助邓玉函学习中文,协助他发明了一种汉语拼音系统。
在中国,邓玉函和其它传教士像中国普通的科技工作者那样工作,几乎无暇传教。他们像小职员那样工作,俸禄微薄,没有任何特权。对于他们工作失误的惩罚,没有任何外交色彩的豁免,皇帝只会因为爱惜人才而从轻发落。他们越来越无法抽身出去传教,只会在工作的间歇,和中国同事谈论一下天主和圣母的故事。
明朝末年的中国人看起来"几乎什么都能接受",传教士的科学知识促使徐光启这样的中国知识分子,改变中国传统的"体用不分"的知识观,给自然科学摘掉了沉重的政治帽子。传教士们也对中国充满了好奇,他们试图理解和发现中国的方方面面,比如邓玉函就试图将中国所有的草药一一研磨蒸馏成粉末剂。当时的北京历局,简直是东方真正的学术中立之地,大可以在那里自由地研究。
而此时的欧洲正处于宗教法庭的黑暗之中。1633年,邓玉函的同学伽利略被罗马教廷判处终身监禁,据说他曾经因为不明的原因拒绝过邓玉函的来华邀请。
当然他们也有威胁,那就是那些方士们利用中国传统星象学对他们的陷害。汤若望,这个曾被顺治皇帝尊为"玛法"(即满语"老爷爷")的老头,在鏊拜当道时,受到中国天文史上最无耻的假方士杨光先的弹劾,险遭凌迟。
17-18世纪上半叶,路易十四时代,被称为"国王的数学家"的新一批耶稣会士由国王和法兰西学院派遣来中国,这时他们身上的宗教色彩就更淡了,他们正好赶上康熙皇帝最开明的当政期,成了皇帝御用的天文学家,数学老师,画家,尼布楚谈判顾问……这是难得的平等交换知识的时代。法国人非常虚心地通过传教士了解中国,康熙成为法国国王的榜样,法国复古的老臣开始效法中国对古老制度的维持,魁奈则从农业中国身上领悟了重农主义的精髓,许多传教士成了汉学家。而中国宫廷则继续让传教士展现他们自然科学方面的才华。
但耶稣会的作为在教廷看来当然是不务正业。1773年,罗马教廷宣布解散耶稣会,其中重要的一个原因就是教廷无法容忍耶稣会"因在亚洲传播知识而忘记了传福音、过于适应当地习俗"的做法。
到了近代,来华的外国传教士和100年前的耶稣会士已经完全不同:大量素质不高的新教教士进入中国,而1814年恢复的耶稣会士的成员也已经非常平庸。梁启超这样的知识分子,开始反对曾是知识载体的西洋宗教。
而现在的讲道,和传播知识就距离更远了。
标签: 闲暇
标签: 感性
易中天的《品人录》中对雍正的介绍让人震撼。雍正,这位中国历史上最具有管理才能、实干精神又最勤奋的人物,令人叹服。
他勤政不息。他没什么个人爱好,特别喜欢的东西是眼镜。在他的宫廷里,四处都备有眼镜,以便随时办公使用。他每天从早到晚,阅读公文无数,朱批能达七八千字,简直就是以工作为生命。
他个性飞扬。他痛恨官场的沽名钓誉、虚伪造作,而将"诚"列为品质的最重要标准。他不能容忍有人投机钻营、沽名钓誉,而他的打击方式也极具个性,比如他惩罚无耻文人钱名世的方式是为他题匾"名教罪人",然后命其悬挂堂上,并地方官定期检查。他的朱批是历朝历代帝王中最具个性的,比如"该、该、该、该"这样充满表情的话语。
他深谙人情世故、观察细致入微,见识过人。他能敏锐感知出隐藏在事物表面后面的真正含义和微妙动机,从而对事物具有自己的独到见解。对以大众舆论考评人才,他说:"舆论二字不但不足凭,竟全然听不得"。因为秉公办事者,必舆论不佳,而好好先生口碑必好得出奇。他对人性和具体的个人也有着深刻的认识,从而能够不被人欺骗和战胜对手。
他极具管理才能。他独创的"密折"制度使他能够通达民情、足不出户而对帝国大小各方面事务心里有数,同时他也能不偏听偏信。他提出"公、忠、廉、能"的选拔人才标准,任用人材不拘一格,使人尽其才,而对平庸之人,也从不迁就。他在整顿吏治一役中的表现可以说是空前绝后,几近完美。在这场战役之前,他就通过调查研究,形成了切实可行的方案。在实施过程中,他通过钦差和极具创意的候补随团方式,组织起调查队伍,然后有条不紊地实施各项战略、策略,前堵后封,步步为营、杀得贪官们是片甲不留。在初战告捷之后,他更是以"费改税"和"高薪养廉"两条基本制度改革,取其根本,最后得到"雍正一朝、无官不清"的成果,令人震服。
对自己的孤独和敌人对他的中伤,他从不后悔,因为他早就知道:"因为秉公办事者,必舆论不佳,而好好先生口碑必好得出奇"。他治国成果的巨大,对照一下他的父亲和儿子的成就就知道了:他的父亲康熙,留下来的是吏治的全面腐败和巨额赤字。雍正用5年时间,肃清旧帐,使国库从8百万两增至5千万两,吏治空前绝后地清。他的儿子乾隆治下,吏治重新彻底腐败,乾隆的宠臣和珅家财8亿两,是法王路易十四财产的14倍。
这就是雍正,清朝超级CEO。他触犯了太多人的利益,留下了不被人理解的声名,但他做出的成绩,总会在历史的陈迹中被人发现,令人赞惜。
Technorati : 雍正
标签: 知性
标签: 技术
标签: 生活
Throwable类
Throwable类对可以作为违例"掷"出的所有东西进行了描述。Throwable对象有两种继承类。Error类代表编译期和系统错误,一般不必特意捕获它们,而Exception类需要捕获,它可以从任何标准Java库的类方法中"掷"出的基本类型,亦可从我们自己的方法以及运行期偶发事件中"掷"出。
Throwable类必须在违例规格中出现,因为fillInStackTrace()会生成一个Throwable对象的句柄。
Exception类
Exception类是所有违例类的基础类,它不提供关于违例太多的信息。它的基础类Throwable提供更多方法以提供更多信息:
String getMessage() : 获得详细的消息。
String toString() : 返回对Throwable的一段简要说明,其中包括详细的消息(如果有的话)。
void printStackTrace(),void printStackTrace(PrintStream) : 打印出Throwable和Throwable的调用堆栈路径。调用堆栈显示出将我们带到违例发生地点的方法调用的顺序。
除此以外,我们还可从Throwable的基础类Object类获得另外一些方法。对于违例控制来说,其中一个可能有用的是getClass(),它的作用是返回一个对象,用它代表这个对象的类。可依次用getName()或toString()查询这个Class类的名字。
捕获
catch(Exception e)
重新"掷"出
throw e; throw e.fillInStackTrace();
重新"掷"出一个违例从而进入更高一级的违例控制器中。
若只是简单地重新掷出当前违例,那么在更高一级的违例控制器中,用printStackTrace()打印出来的信息会与违例的起源地对应,而不是与重新掷出它的地点对应。调用fillInStackTrace()使当前堆栈的信息填充到原来的违例对象里然后掷出。
可以从一个已经捕获的违例"掷"出一个不同的违例。这样做会得到与使用fillInStackTrace()类似的效果:与违例起源地有关的信息会全部丢失,留下的是与新的throw有关的信息。
RuntimeException
RuntimeException属于Java进行的标准运行期检查的一部分。没必要专门写一个违例规范指出它可能会被"掷"出。而由于它们是用于指出编程中的错误的,在默认情况下会自动得到处理,所以也几乎永远不必专门来捕获它。
假若一个RuntimeException发生而没有被捕获直到main(),那么当程序退出时,Java会为那个违例自动调用printStackTrace(),报错。
创建自己的违例
由于默认构建器将一定被自动调用,所以衍生类的构建器必须在自己的违例规范中声明所有的基础类构建器违例,这就是说要强迫衍生类方法遵守基础类方法的违例规范。通过这种强迫,对象的替换可保持连贯性。(OK to add new exceptions for constructors, but you must deal with the base constructor exceptions )
一个方法的衍生类版本可以不产生任何违例--即便基础类版本要产生违例。
假如我们明确操作一个对象,编译器就会强迫我们只捕获特定于那个类的违例。但假如我们上溯造型到基础类型,编译器就会强迫我们捕获针对基础类的违例。通过所有这些限制,违例控制代码的"健壮"程度获得了大幅度改善。
非构造器方法的"违例规范接口"可能在继承和覆盖时变得更"窄",但它不会变得更"宽"--这与继承时的类接口规则是正好相反的。(Interface CANNOT add exceptions to existing methods from the base class)
finally块
无论是否在try块中发生违例,finally都会被执行,都会在违例控制机制转到更高级别之前执行。即使调用了break和continue语句,finally语句也会执行。值得注意的是,与作上标签的break和continue一道,finally排除了Java对goto跳转语句的需求。
特别留意构建器中的违例
构建器将对象置于一个安全的起始状态,但它可能执行一些操作--如打开一个文件。除非用户完成对象的使用,并调用一个特殊的清除方法,否则那些操作不会得到正确的清除。若从一个构建器内部"掷"出一个违例,这些清除行为也可能不会正确地发生。所有这些都意味着在编写构建器时,我们必须特别加以留意。
违例和控制器的匹配
在违例和它的控制器之间,并不需要非常精确的匹配。一个衍生类对象可与基础类的一个控制器相配。这一点非常重要,因为一旦决定为一个方法添加更多的违例,而且它们都是从相同的基础类继承的,那么客户程序员的代码就不需要更改。至少能够假定它们捕获的是基础类。
Technorati : java
标签: 技术
最近在学Java,总的感觉不错。《Think in Java》这本很好的的书,到第七章开始产生难度,需要做些笔记才不至于看完又忘。下面是第七章-多形和第八章-数组的笔记。
第7章:多形
多形性将接口从具体的实施细节中分离出来。通过继承实现。
Java中绑定的所有方法都采用后期绑定技术,除非一个方法已被声明成final。
把一个方法声明成final能防止其他人覆盖那个方法。更重要的是,它可"关闭"动态绑定,清楚地告诉编译器不需要进行动态绑定。这样一来,编译器就可为final方法调用生成效率更高的代码。
上溯造型
可将一个对象作为它自己的类型使用,或者作为它的基础类型的一个对象使用。取得一个对象句柄,将其作为基础类型句柄使用的行为就叫作"上溯造型"。
static void tune(Instrument3 i) {
i.play();
}
抽象方法
"抽象方法"属于一种不完整的方法,只含有一个声明,没有方法主体。抽象方法声明时采用语法: abstract void X();
包含了抽象方法的一个类叫作"抽象类"。如果一个类里包含了一个或多个抽象方法,类就必须指定成abstract(抽象)。否则,编译器会向我们报告出错消息。
接口
"interface"(接口)关键字使抽象的概念更深入了一层。它防止客户程序员制作这个类的一个对象,规定它仅仅是一个接口。
接口也可以包含基本数据类型的数据成员,但它们都默认为static和final。static内部类也可以成为接口的一部分。
接口也可以通过extends关键字继承。利用继承技术,可方便地为一个接口添加新的方法声明,也可以将几个接口合并成一个新接口,即"多重继承"。
Java的"多重继承"通过接口实现:将所有接口名置于implements关键字的后面,并用逗号分隔它们。这是接口最关键的作用,也是使用接口最重要的一个原因:可根据需要使用多个接口,而且每个接口都会成为一个独立的类型,可对其进行上溯造型。能上溯造型至多个基础类。
由于置入一个接口的所有字段都自动具有static和final属性,所以接口是对常数值进行分组的一个好工具,它具有与C或C++的enum非常相似的效果。注意根据Java命名规则,拥有固定标识符的static final基本数据类型(亦即编译期常数)都全部采用大写字母(用下划线分隔单个标识符里的多个单词)。
接口中的字段会自动具备public属性,所以没必要专门指定。
内部类
利用private内部类,类设计人员可将具体的实施细节完全隐藏起来。除此以外,因为他们不能被访问,使Java编译器也有机会生成效率更高的代码。而普通(非内部)类不可设为private或protected--只允许设为public或者"友好的"。
若试图定义一个匿名内部类,并想使用在匿名内部类外部定义的一个对象,则编译器要求外部对象为final属性。此外,通过Java 1.1的实例初始化,我们可以有效地为一个匿名内部类创建一个构建器。
一个重要的事实是,创建自己的内部类时,那个类的对象同时拥有指向封装对象的一个链接,所以它们能访问那个封装对象的成员--毋需取得任何资格。它们拥有对封装类所有元素的访问权限。它们可以访问封装类的方法与字段,就象已经拥有了它们一样。这一特征对我们来说是非常方便的。若想生成外部类对象的句柄,就要用一个点号以及一个this来命名外部类。
为告诉创建某个内部类的一个对象,需要在new表达式中提供指向外部类对象的一个句柄,然后利用这个句柄生成内部类的对象,比如:Parcel11 p = new Parcel11(); Parcel11.Contents c = p.new Contents(); 这是由于内部类的对象已同创建它的外部类的对象"默默"地连接到一起。然而,如果生成一个static内部类,就不需要指向外部类对象的一个句柄。
从内部类继承:由于内部类构建器必须同封装类对象的一个句柄联系到一起,所以从一个内部类继承的时候,封装类的"秘密"句柄必须获得初始化,而且在衍生类中不再有一个默认的对象可以连接。需要采用一种特殊的语法,明确建立这种关联。
public class InheritInner
extends WithInner.Inner {
InheritInner(WithInner wi) {
wi.super();
}
当我们从外部类继承的时候,没有任何额外的内部类继承下去。如果需要继承,需要"明确"地从内部类继承:public class Yolk extends Egg2.Yolk
内部类编译为class的时候,它的名字遵守一种严格的形式:先是封装类的名字,再跟随一个$,再跟随内部类的名字。如果内部类是匿名的,那么编译器会简单地生成数字,把它们作为内部类标识符使用。
用内部类的一大好处是为了实现控制框架。"控制框架"属于应用程序框架的一种特殊类型,受到对事件响应的需要的支配。控制框架的设计宗旨是将不同的代码方便地隔离开。比如Java 1.1 AWT就是属于一种控制框架,它通过内部类完美地解决了GUI的问题。
我们通过创建不同的Event内部子类,从而表达出不同的行动。这里正是内部类大显身手的地方。它们允许我们做两件事情:
(1) 在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。
(2) 内部类使我们具体的实施变得更加巧妙,因为它能方便地访问外部类的任何成员。
一条常规的设计准则是:用继承表达行为间的差异,并用成员变量表达状态的变化。
构建器
基础类的构建器肯定会在它的衍生类的构建器中被调用,而且逐渐向上链接,使每个基础类使用的构建器都能得到调用。之所以要这样做,是由于构建器负有一项特殊任务:检查对象是否得到了正确的构建。由于一个衍生类只能访问它自己的成员,不能访问基础类的成员(这些成员通常都具有private属性),所以只有基础类的构建器在初始化自己的元素时才知道正确的方法以及拥有适当的权限。所以,必须令所有构建器都得到调用。
构建器的调用遵照下面的顺序:
前提:在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。
(1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然后是下一个衍生类,等等。直到抵达最深一层的衍生类。
(2) 按声明顺序调用成员初始化模块。
(3) 调用衍生构建器的主体。
设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final属性的那些方法(也适用于private方法,它们自动具有final属性)。这些方法不能被覆盖,所以不会出现潜在的调用继承类方法的问题。
消除
若认为自己的句柄可以被消除,可以将其设为null,使垃圾收集器能够正常地清除它们。
如果需要自己写finalize,那么在覆盖衍生类的finalize()时,务必记住调用finalize()的基础类版本。
收尾的顺序与初始化顺序正好相反。我们首先执行衍生类的收尾,再是基础类的收尾。这是由于衍生类的收尾可能调用基础类中相同的方法,要求基础类组件仍然处于活动状态。
第8章 对象的容纳
数组
在Java中,无论使用的是数组还是集合,都会进行范围检查--若超过边界,就会获得一个RuntimeException(运行期违例)错误。
创建一个数组时,可令其容纳一种特定的类型。这意味着可进行编译期类型检查,预防自己设置了错误的类型,或者错误指定了准备提取的类型。
在Java 1.1中,可动态创建想作为参数传递的数组,如 hide(new Weeble[] {new Weeble(), new Weeble() });
假如容纳的是基本数据类型,必须采用数组。
集合
四种类型的"集合类":Vector(矢量)、BitSet(位集)、Stack(堆栈)以及Hashtable(散列表)。
所有集合类都能自动改变自身的大小。
(1) 将一个对象句柄置入集合时,类型信息会被抛弃,所以任何类型的对象都可进入集合--即便特别指示它只能容纳特定类型的对象。举个例子来说,虽然指示它只能容纳猫,但事实上任何人都可以把一条狗扔进来。
(2) 由于类型信息不复存在,所以集合能肯定的唯一事情就是自己容纳的是指向一个对象的句柄。正式使用它之前,必须对其进行造型,使其具有正确的类型。
Vector
Vector使用是非常简单:先创建一个,Vector cats = new Vector(); 再用addElement()置入对象,以后用elementAt()取得那些对象(注意Vector有一个size()方法)。v.elements()返回它的枚举。addElement()和elementAt()都具有final属性。
参数化类型:在编译期间拥有特定的类型信息,在Java仍不支持。
Enumeration:
(1) 用一个名为elements()的方法要求集合为我们提供一个Enumeration。我们首次调用它的nextElement()时,这个Enumeration会返回序列中的第一个元素。
(2) 用nextElement()获得下一个对象。
(3) 用hasMoreElements()检查序列中是否还有更多的对象。
BitSet
BitSet的最小长度是一个长整数(Long)的长度:64位。
BitSet bb = new BitSet(); bb.set(i); bb.clear(i); bb.size(); bb.get(j)
Stack
Stack实现一个LIFO(后入先出)序列。
Stack stk = new Stack(); stk.push("entry"); stk.addElement("The last line"); stk.empty();stk.elementAt(5)); stk.pop();
Hashtable
Hashtable是一种"关联数组",允许我们将任何对象关联起来。
size();isEmpty();put(Object key, Object value);get(Object key);remove(Object Key)。还可以使用枚举技术:keys()产生对键的一个枚举(Enumeration);elements()产生对所有值的一个枚举。
所有对象都有一个散列码,通过根类Object方法hashCode()获得,然后用它快速查找键。性能大幅提升。
Hashtable ht = new Hashtable(); ht.containsKey(r));ht.get(r));ht.put(r, new Counter());
Hashtable toString()方法能遍历所有键-值对,并为每一对都调用toString()。
因为不可以将任何基本数据类型置入集合里,所以需要封装类。比如Integer。然而,对Java封装类能做的唯一事情就是将其初始化成一个特定的值,然后读取那个值。也就是说,一旦封装类对象创建,就没有办法改变它。这使得我们有时不得不创建一个新类,用它来填充Hashtable。
创建Hash Key类
为了在散列表中将自己的类作为键使用,必须同时覆盖hashCode()和equals()。
public boolean equals(Object o) {
return (o instanceof Groundhog2)
&& (ghNumber == ((Groundhog2)o).ghNumber);
}
系统 Hashtable类型 Properties(属性)
Properties p = System.getProperties();
p.list(System.out);
list()可将内容发给我们选择的任何流式输出。还有一个save()方法可将属性列表写入一个文件,以便日后用load()方法读取。
构建自己的排序算法
interface Compare {
boolean lessThan(Object lhs, Object rhs);
boolean lessThanOrEqual(Object lhs, Object rhs);
}
public class SortVector extends Vector {
private Compare compare; // To hold the callback
public SortVector(Compare comp) {
compare = comp;
}
private void quickSort(int left, int right) {
if(right > left) {
Object o1 = elementAt(right);
int i = left - 1;
int j = right;
while(true) {
while(compare.lessThan(
elementAt(++i), o1))
;
while(j > 0)
if(compare.lessThanOrEqual(
elementAt(--j), o1))
break; // out of while
if(i >= j) break;
swap(i, j);
}
swap(i , right);
quickSort(left, i-1);
quickSort(i+1, right);
}
}
JGL
ObjectSpace公司设计了Java版本的"通用集合库"(从前叫作"Java通用库",即JGL;JGL实现了许多功能,可满足对一个集合库的大多数常规需求,它与C++的模板机制非常相似。JGL包括相互链接起来的列表、设置、队列、映射、堆栈、序列以及反复器,它们的功能比Enumeration(枚举)强多了。同时提供了一套完整的算法,如检索和排序等。在某些方面,ObjectSpace的设计也显得比Sun的库设计方案"智能"一些。举个例子来说,JGL集合中的方法不会进入final状态,所以很容易继承和改写那些方法。JGL已包括到一些厂商发行的Java套件中,而且ObjectSpace公司自己也允许所有用户免费使用JGL,包括商业性的使用。详细情况和软件下载可访问http://www.ObjectSpace.com。与JGL配套提供的联机文档做得非常好,可作为自己的一个绝佳起点使用。
新的Java 1.2集合库
有些名字进行了修改,更接近于通俗:用"反复器"(Inerator)代替了"枚"(Enumeration)。
新的集合库将其分割成两个明确的概念:
(1) 集合(Collection):一组单独的元素,通常应用了某种规则。List(列表)必须按特定的顺序容纳元素,而Set(集)不可包含任何重复的元素。
(2) 映射(Map):一系列"键-值"对(这已在散列表身上得到了充分的体现)。可以方便地查看Map的某个部分:只需创建一个集合,然后用它表示那一部分即可。这样一来,Map就可以返回自己键的一个Set、一个包含自己值的List或者包含自己"键-值"对的一个List。和数组相似,Map可方便扩充到多个"维",毋需涉及任何新概念。只需简单地在一个Map里包含其他Map(后者又可以包含更多的Map,以此类推)。
Collection c = new ArrayList(); c.add(Integer.toString(i));
Iterator it = c.iterator(); it.hasNext(); it.next(); it.remove() - 新方法,可删除由Iterator生成的上一个元素。所以每次调用next()的时候,只需调用remove()一次。
List, ArrayList, LinkedList, ListIterator
ArrayList(以及Vector)是一个数组;LinkedList是常规的双重链接列表,每个对象都包含了数据以及指向列表内前后元素的句柄。假如想在一个列表中部进行大量插入和删除操作,那么LinkedList无疑是最恰当的选择(LinkedList还有一些额外的功能,建立于AbstractSequentialList中)。若进行随机访问(即get())以及循环反复,就情愿选择ArrayList,它的速度要快一些。我们最好的做法也许是先选择一个ArrayList作为自己的默认起点。以后若发现由于大量的插入和删除造成了性能的降低,再考虑换成LinkedList不迟。
Set, HashSet, TreeSet
作为另一个例子,Set既可作为一个ArraySet实现,亦可作为HashSet实现。ArraySet是由一个ArrayList后推得到的,设计成只支持少量元素,特别适合要求创建和删除大量Set对象的场合使用。然而,一旦需要在自己的Set中容纳大量元素,ArraySet的性能就会大打折扣。写一个需要Set的程序时,应默认选择HashSet。而且只有在某些特殊情况下(对性能的提升有迫切的需求),才应切换到ArraySet。 进行add()以及contains()操作时,HashSet显然要比ArraySet出色得多,而且性能明显与元素的多寡关系不大。一般编写程序的时候,几乎永远用不着使用ArraySet。
Map, HashMap, TreeMap
当我们使用Map时,首要的选择应该是HashMap。只有在极少数情况下才需要考虑其他方法。
使用TreeMap通常并不作为Map使用,而是作为创建顺序列表的一种途径。树的本质在于它总是顺序排列的,不必特别进行排序。一旦填充了一个TreeMap,就可以调用keySet()来获得键的一个Set"景象"。然后用toArray()产生包含了那些键的一个数组。随后,可用static方法Array.binarySearch()快速查找排好序的数组中的内容。
TreeMap的创建速度比其他两种类型明显快得多,所以如果需要创建大量Map,而且只有在以后才需要涉及大量检索操作,那么最佳的策略就是:创建和填充TreeMap;以后检索量增大的时候,再将重要的TreeMap转换成HashMap--使用HashMap(Map)构建器。同样地,只有在事实证明确实存在性能瓶颈后,才应关心这些方面的问题--先用起来,再根据需要加快速度。
排序
Random.nextBytes()用随机选择的字节填充数组自变量(没有对应的Random方法用于创建其他基本数据类型的数组)。获得一个数组后,便可执行sort()或者binarySearch()。一个重要的警告是:若在执行binarySearch()之前不调用sort(),便会发生不可预测的行为,其中甚至包括无限循环。
对String的排序以及搜索是相似的,但在运行程序的时候,我们会注意到一个有趣的现象:排序遵守的是字典顺序,亦即大写字母在字符集中位于小写字母的前面。因此,所有大写字母都位于列表的最前面,后面再跟上小写字母--Z居然位于a的前面。似乎连电话簿也是这样排序的。
针对Object数组,可在binarySearch()和sort()中加入另一个参数:实现Comparator接口(新集合库的一部分)的一个对象,并用它的单个compare()方法进行比较。注意:若用自己的Comparator来进行一次sort(),那么在使用binarySearch()时必须使用那个相同的Comparator。
Arrays类提供了另一种sort()方法,不需加入参数,它要求Object实现Comparable接口及接口的compareTo()方法。
排序和搜索列表(List)与排序搜索数组形式相同,只是用于排序和搜索列表的静态方法包含在类Collections中。
只读版本
通常,创建Collection或Map的一个"只读"版本显得更有利一些。Collections类允许我们达到这个目标,方法是将原始容器传递进入一个方法,并令其传回一个只读版本。这个方法共有四种变化形式,分别用于Collection(如果不想把集合当作一种更特殊的类型对待)List、Set以及Map。e.g. c = Collections.unmodifiableCollection(c);
同步版本
为了支持"多线程",Collections类提供了对整个容器进行自动同步的一种途径。Map m = Collections.synchronizedMap(new HashMap()); 这种直接传递新容器的方法可避免不慎暴露出未同步的版本。同步提供了防止多个进程同时修改一个容器内容的机制。它集成了一套解决机制,若探测到有其他方面也准备修改容器,便会立即产生一个ConcurrentModificationException(并发修改违例)。这一机制被称为"立即失败"--它并不用更复杂的算法在"以后"侦测问题,而是"立即"产生违例。
System.currentTimeMillis();
Technorati : Java
标签: 技术