开心一笑
【小明喜欢上一个刚来的女同事,她是医院里的检验师,为了套近乎就经常跑到她科室去倒开水喝,熟了后就发现她随身的小包包里总放着一把小水果刀,于是我问她:你总放把小刀包里干嘛?她答道:下夜班防身啊!虽然刀小,但是我清楚的知道人的大动脉在哪里!我去!这特么以后还敢撩吗?】
提出问题
如何优雅的使用数组???
唯美爱情图片解决问题
初始化数组格式选择
程序清单 2-1
@Test
public void test(){
//第一种初始化数组格式
String[] a = new String[5];
//第二种初始化数组格式
String b[] = new String[5];
}
通常情况下,大部分的程序员都会选择用第一种初始化数组的格式。但我确实碰到过有少数的开发人员用第二种初始化数组格式。这里我建议使用第一种,主要原因是:大多数人都使用第一种,自然,我们也要少数服从多数。而且从字面意思来看,String[] a 表示:初始化一个字符串(String)数组([]) a,读起来更加顺口。
数组是性能调优的一大法宝
数组和集合,我们都可以简单的理解为篮子。不同的篮子可以装不同的东西。
对于基本类型,建议你使用数组篮子,相对于集合篮子,它效率更高。具体实例如下:
程序清单 2-1
@Test
public 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 nanosecond
cloneCopy cost 53490 nanosecond
arraysCopyOf cost 119946 nanosecond
systemArraycopy cost 39712 nanosecond
多运行几次,我们得出数组复制效率:
System.arraycopy > clone > Arrays.copyOf > for
综上所述,当复制大量数据时,使用System.arraycopy()命令。
警惕二维数组的拷贝
因为java中没有二维数组的概念,只有数组的数组。所以对数组的数组进行拷贝,无论对clone或者arraycopy,都只是拷贝其中的引用。事实上,还是指向同一个值。具体实例如下:
程序清单 2-1
@Test
public 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
@Test
public 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 :60
the origin of name :ay
the origin of girl id :30
the origin of girl name :al
the copy of id :change_60
the copy of name :change_ay
the copy of girl id :change_girl_30
the 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_60
the origin of name :change_ay
the origin of girl id :change_girl_30
the origin of girl name :change_girl_al
the copy of id :change_60
the copy of name :change_ay
the copy of girl id :change_girl_30
the copy of girl name :change_girl_al
可以证实,普通的clone只是浅拷贝而已。
一维数组替换二维数组,提高性能
在项目开发中,如果有碰到二维数组,我们要机智的把它转化为一维数组。因为一维数组比较容易理解,而且性能会比二维数组高。
@Test
public 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 :1487563038580
consume time is : 19
start time is :1487563038600
consume 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(书籍)
其他
如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎点赞、顶、欢迎留下宝贵的意见、多谢支持!
网友评论