首页 > 学院 > 开发设计 > 正文

【优雅编程之道】之数组的7点建议

2019-11-08 01:45:19
字体:
来源:转载
供稿:网友

开心一笑

【小明喜欢上一个刚来的女同事,她是医院里的检验师,为了套近乎就经常跑到她科室去倒开水喝,熟了后就发现她随身的小包包里总放着一把小水果刀,于是我问她:你总放把小刀包里干嘛?她答道:下夜班防身啊!虽然刀小,但是我清楚的知道人的大动脉在哪里!我去!这特么以后还敢撩吗?】

提出问题

如何优雅的使用数组???

唯美图片

解决问题

初始化数组格式选择

程序清单 2-1 @Testpublic void test(){ //第一种初始化数组格式 String[] a = new String[5]; //第二种初始化数组格式 String b[] = new String[5];}

通常情况下,大部分的程序员都会选择用第一种初始化数组的格式。但我确实碰到过有少数的开发人员用第二种初始化数组格式。这里我建议使用第一种,主要原因是:大多数人都使用第一种,自然,我们也要少数服从多数。而且从字面意思来看,String[] a 表示:初始化一个字符串(String)数组([]) a,读起来更加顺口。

数组是性能调优的一大法宝

数组和集合,我们都可以简单的理解为篮子。不同的篮子可以装不同的东西。 对于基本类型,建议你使用数组篮子,相对于集合篮子,它效率更高。具体实例如下:

程序清单 2-1 @Testpublic void test(){ int sum = 0; //数组装int基本类型 int[] baseTypeArray = new int[10000]; for(int i=0,len = baseTypeArray.length;i < len;i++){ //不存在自动装箱和拆箱的操作 sum = sum + baseTypeArray[i]; } //集合装包装类型 List<Integer> objectTypeList = new ArrayList<>(10000); for(int i=0,len = objectTypeList.size();i < len;i++){ //在这里有自动装箱和拆箱的操作,效率低 sum = sum + objectTypeList.get(i); }}

四种数组复制方式的性能比较和抉择

数组copy有很多种方法,效率不一。我们先看下面具体实例:

程序清单 2-1 /** * 测试4种数组复制效率比较 * @author 阿毅 * @date 2017/2/7. */public class AyTest { PRivate static final byte[] buffer = new byte[1024*10]; static { for (int i = 0; i < buffer.length; i++) { buffer[i] = (byte) (i & 0xFF); } } private static long startTime; public static void main(String[] args) { startTime = System.nanoTime(); byte[] newBuffer = new byte[buffer.length]; for(int i=0;i<buffer.length;i++) { newBuffer[i] = buffer[i]; } calcTime("forCopy"); startTime = System.nanoTime(); byte[] newBuffer2 = buffer.clone(); calcTime("cloneCopy"); startTime = System.nanoTime(); byte[] newBuffer3 = Arrays.copyOf(buffer, buffer.length); calcTime("arraysCopyOf"); startTime = System.nanoTime(); byte[] newBuffer4 = new byte[buffer.length]; System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); calcTime("systemArraycopy"); } private static void calcTime(String type) { long endTime = System.nanoTime(); System.out.println(type + " cost " +(endTime-startTime)+ " nanosecond"); }}

运行结果:

forCopy cost 711576 nanosecondcloneCopy cost 53490 nanosecondarraysCopyOf cost 119946 nanosecondsystemArraycopy cost 39712 nanosecond

多运行几次,我们得出数组复制效率:

System.arraycopy > clone > Arrays.copyOf > for

综上所述,当复制大量数据时,使用System.arraycopy()命令。

警惕二维数组的拷贝

因为java中没有二维数组的概念,只有数组的数组。所以对数组的数组进行拷贝,无论对clone或者arraycopy,都只是拷贝其中的引用。事实上,还是指向同一个值。具体实例如下:

程序清单 2-1 @Testpublic void test(){ //一维数组的拷贝 int[] a = {5,2,0,1,3,1,4}; int[] b = a.clone(); b[0]=10; System.out.println(a[0] + " copy " + b[0]); //打印结果:5 copy 10 //二维数组的拷贝:{5,2,0} 和 {1,3,1,4}这两个一维数组都有引用指向它们 int[][] c = {{5,2,0},{1,3,1,4}}; int[][] d = c.clone(); d[0][0]=10; System.out.println(c[0][0] + " coyp " + d[0][0]); //打印结果:10 coyp 10 //二维数组的拷贝:{5,2,0} 和 {1,3,1,4}这两个一维数组都有引用指向它们 int[][] e = {{5,2,0},{1,3,1,4}}; int[][] f = {{},{}}; System.arraycopy(e, 0, f, 0, e.length); f[0][0]=10; System.out.println(e[0][0] + " coyp " + f[0][0]); //打印结果:10 coyp 10}

从上面的例子可以看出,利用clone和System.arraycopy进行二维数组的拷贝,都是浅拷贝。在真实项目中,我们要警惕使用。

数组的深复制及优雅实现

在上面的警惕二维数组的拷贝中我们已经知道,clone和System.arraycopy都是浅拷贝,下面提供一些深拷贝的优雅方法:

例:

程序清单 2-1 @Testpublic void test(){ Boy[] boys = new Boy[1]; Boy boy = new Boy("60","ay",new Girl("30","al")); boys[0] = boy; //利用org.apache.commons.lang3.SerializationUtils进行深度clone Boy[] coypBoys = (Boy[])SerializationUtils.clone(boys); //clone是浅clone //Boy[] coypBoys = boys.clone(); coypBoys[0].id = "change_60"; coypBoys[0].name = "change_ay"; coypBoys[0].girl.id = "change_girl_30"; coypBoys[0].girl.name = "change_girl_al"; for(int i=0,len = boys.length;i<len;i++){ System.out.println("the origin of id :" + boys[i].id); System.out.println("the origin of name :" + boys[i].name); System.out.println("the origin of girl id :" + boys[i].girl.id); System.out.println("the origin of girl name :" + boys[i].girl.name); } for(int i=0,len = coypBoys.length;i<len;i++){ System.out.println("the copy of id :" + coypBoys[i].id); System.out.println("the copy of name :" + coypBoys[i].name); System.out.println("the copy of girl id :" + coypBoys[i].girl.id); System.out.println("the copy of girl name :" + coypBoys[i].girl.name); }}

执行结果:

the origin of id :60the origin of name :aythe origin of girl id :30the origin of girl name :althe copy of id :change_60the copy of name :change_aythe copy of girl id :change_girl_30the copy of girl name :change_girl_al

上面例子中,我们利用 apache 提供的工具类 SerializationUtils 进行深度clone,避免重复造轮子。有一点需要注意的是,Boy 和 Girl 类都要实现 Serializable 接口。具体如下:

程序清单 2-1 class Boy implements Serializable{ public String id; public String name; public Girl girl; Boy(String id,String name,Girl girl){ this.id = id; this.name = name; this.girl = girl; }}class Girl implements Serializable{ public String id; public String name; Girl(String id,String name){ this.id = id; this.name = name; }}

我们把 //Boy[] coypBoys = boys.clone();注释打开,运行程序,结果如下:

the origin of id :change_60the origin of name :change_aythe origin of girl id :change_girl_30the origin of girl name :change_girl_althe copy of id :change_60the copy of name :change_aythe copy of girl id :change_girl_30the copy of girl name :change_girl_al

可以证实,普通的clone只是浅拷贝而已。

一维数组替换二维数组,提高性能

在项目开发中,如果有碰到二维数组,我们要机智的把它转化为一维数组。因为一维数组比较容易理解,而且性能会比二维数组高

@Testpublic void test(){ int arr[][] = new int[1000][1000]; long startTime = System.currentTimeMillis(); System.out.println("start time is :" + startTime); for(int i=0;i<1000;i++){ //在for循环中进行方法调用,影响性能 for(int j=0;j<arr[0].length;j++){ arr[i][j] = j; } } long endTime = System.currentTimeMillis(); System.out.println("consume time is : " + (endTime - startTime)); // -------------------------------------------------------------- startTime = System.currentTimeMillis(); System.out.println("start time is :" + startTime); //这里是重点 int len = arr[0].length; for(int i=0;i<1000;i++){ for(int j=0;j<len;j++){ arr[i][j] = j; } } endTime = System.currentTimeMillis(); System.out.println("consume time is : " + (endTime - startTime));}

运行结果:

start time is :1487563038580consume time is : 19start time is :1487563038600consume time is : 9

如果二维数组没办法转化为一维数组,那么我们要极力避免在循环体内进行方法调用,如上面例子中的arr[0].length,每循环一次都会执行一次length方法,执行时长也多出10ms。因此,我们要把arr[0].length抽出来,减少方法执行次数。

考虑用集合,栈和队列来代替数组

我们建议用集合,栈和队列等按顺序存取元素的数据结构来取代数组。在之前,我们提过:对于存放基本类型,优先选择用数组。而对于存放其他类型的数据,我们建议用集合,栈和队列。具体原因如下:在数组里随机访问就像在程序里随机使用goto语句一样,这样的访问很容易变得难以管理且容易出错。要证明其是否正确也困难。

读书感悟

来自山下英子《断舍离》

断绝不需要的东西、舍弃多余的废物、脱离对物品的执念。断=断绝想要进入自己家的不需要的东西。舍=舍弃家里到处泛滥的破烂儿。离=脱离对物品的执念,处于游刃有余的自在的空间。断舍离,就是透过整理物品了解自己,整理内心的混沌,让人生舒适的行动技术。换句话说,就是利用收拾家里的杂物来整理内心的废物,让人生转而开心的方法。要是自己能随便凑合着用一个东西,那别人也会用随便的态度来对待你。断舍离的主角并不是物品,而是自己,而时间轴永远都是现在。选择物品的窍门,不是“能不能用”,而是“我要不要用”,这一点必须铭刻在心。不管东西有多贵,有多稀有,能够按照自己是否需要来判断的人才够强大。能够放开执念,人才能更有自信。这就是让物品当了主角的状态……物品原本是因为“我用”才有价值。可多数人都说“眼镜可以用”,“筷子可以用”,拿物品当了主语。这是把主角的位子拱手让给了物品,把焦点聚集在物品上的状态……收纳这些东西,不过是在做垃圾分类。

经典故事

【甲向乙诉苦:“上星期,一粒沙子钻进了我妻子的眼睛,花50元请医生才把它清理出来。”乙不屑地说:“那算得了什么,上星期,一件皮大衣入了我妻子的眼,我花费了3000元。”

永远不要以为自己的境遇是最值得说的,你的听众会认为他的境遇更值得说,因为人是以自我为中心的动物。】

大神文章

【1】Thinking in Java (书籍) 【2】Agile Java(书籍) 【3】编写高质量代码:改善Java程序的151个建议(书籍) 【4】Java程序性能优化 让你的Java程序更快、更稳定(书籍) 【5】Effective Java中文版 第2版.Joshua Bloch(书籍)

其他

如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎点赞、顶、欢迎留下宝贵的意见、多谢支持!


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表