杂记
首先说下高兴的事情,前两天拿到了第一张offer了,不用担心自己找不到工作了。但是这个工作是在深圳,本人是武汉人,还是挺不方便的,所以还在继续的找工作中
不高兴的事情,今天有场面试,要带上成绩单,于是去学工办去打印成绩单,靠,那女的 的态度真心让人不爽,应该是个研究生,郁闷。。。
OK,开始说技术把。
这些天一直在各种宣讲会,笔试面试,各种奔波,关键是我们这个校区太偏,所以基本一天都耗在公交上了,因此这几天学的东西有限:
1.自优化的代码:
这里所谓的优化,并不是对整个项目的优化,而是对自己写的代码进行优化,知道怎么写效率会高些,主要针对字符串
(1)就是String的split方法 对比StringTokenizer类,都是用指定的分隔符分解String,但是在效率上有差异
首先给出测试代码:
@Test public void test1() { String orgStr = null; StringBuffer sb = new StringBuffer(); for(int i=0;i<100000;i++){ sb.append(i); sb.append(","); } orgStr = sb.toString(); long start = System.currentTimeMillis(); String[] strs = orgStr.split(","); int count = 0; for(String s:strs) { count++; } System.out.println(count); long end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); StringTokenizer st = new StringTokenizer(orgStr,","); count = 0; while(st.hasMoreTokens()) { String str = st.nextToken(); count++; } System.out.println(count); end = System.currentTimeMillis(); System.out.println(end-start); }
运行后的测试结果为
100000
38
100000
21
可以看到,当String很长,而且分解得到的String很多的时候,StringTokenizer的效率要高点。
但是 : 用StringTokenizer 明显比直接用split方法要复杂一些
因此,在不是很要求效率的情况下,还是使用split方法,这个代码也看起来清晰一些,毕竟有时候代码的清晰比效率更重要一些
(2)字符串的连接
这个相信大家都知道,String是final的,连接字符串很多的话最好有StringBuilder(单线程),或者StringBuffer(多线程)
然而我最近在一篇文章中看到了还可以用String的 concat方法来连接字符串,下面对比下这些连接方式的快慢
@Test public void testStringConcat () { String str = null; String result = ""; long start = System.currentTimeMillis(); for(int i=0;i<10000;i++){ str = str + i; } long end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); for(int i=0;i<10000;i++){ result = result.concat(String.valueOf(i)); } end = System.currentTimeMillis(); System.out.println(end-start); start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for(int i=0;i<10000;i++){ sb.append(i); } end = System.currentTimeMillis(); System.out.println(end-start); }
最后运行的结果为:
219
87
0
很明显,直接用+连接字符串耗时更长,但是按道理说这样写的代码编译器会帮我们进行优化,怎么还是耗这么长时间呢?按照那篇博客上看到的,是因为编译器不够智能,虽然会使用StringBuilder(StringBuffer)来连接,但是是每一次都建立一个新的StringBuilder(~)对象,我不知道是不是这样的
然后concat方法比直接用+效率要高,StringBuilder效率是最高的。
所以,我感觉,在想优化的地方,不要怕麻烦,使用StringBuilder或者StringBuffer,连接次数很少的地方还是使用+,感觉使用concat方法挺麻烦的.
(3)对局部变量的操作比全局变量的效率要高
测试代码:
public static int b = 0; @Test public void testVariableCompare () { int a = 0; long starttime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ a++;//在函数体内定义局部变量 } System.out.println(System.currentTimeMillis() - starttime); starttime = System.currentTimeMillis(); for(int i=0;i<1000000;i++){ b++;//在函数体内定义局部变量 } System.out.println(System.currentTimeMillis() - starttime); }
运行结果:
2
3
从结果确实可以看出来这点,但是好像差别不是很大,但是还是可以注意一下这个点,在不破坏代码的可读性和清晰的时候,尽可能的使用局部变量把,我好像想到了effective java里面的一个建议,是类和成员的可访问性最小化,就是说如果一个变量只在for循环里面用到的话,就没必要在外面定义它等等。。。
(4)位运算优先
这个是肯定的,cpu是天然支持位运算的,中间不用任何的转化,测试代码
@Test public void testOperator() { long start = System.currentTimeMillis(); long a=1000; for(int i=0;i<10000000;i++){ a*=2; a/=2; } System.out.println(a); System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); for(int i=0;i<10000000;i++){ a<<=1; a>>=1; } System.out.println(a); System.out.println(System.currentTimeMillis() - start); }
结果如下:
1000
22
1000
5
很明显,位运算比*,/运算快了不止一点,所以之前在List源码里面看到扩展数组的时候就是用的位运算
OK,自由化就看了这么多,接下来的内容是java8的更新
2.java8
在之前的博文里面已经写过java8方面的内容了,现在写的是之前没有写到的,主要是java.util.stream包里面的的内容
之前确实看到util里面多了个stream,但是不知道是干什么的,现在看了一篇博客后,会简单的使用了
使用如下:
@Test public void test() { List<String> list = new ArrayList<>(); list.addAll(Arrays.asList("first","second","third")); long count = list.stream().filter(data -> !data.equals("first")).count(); System.out.println(count); List<String> list1 = list.stream().map(data -> data + data).collect(Collectors.toList()); list1.forEach(System.out::println); List<String> list2 = list.stream().map(data -> data.toUpperCase()).collect(Collectors.toList()); list2.forEach(System.out::println); }
得到的结果:
2
firstfirst
secondsecond
thirdthird
FIRST
SECOND
来分析下这段代码,现在每个List,Set,Map.好像都有一个stream方法,这个方法会建立一个流(注意,这个流跟inputstream没关系,也不一样),然后就能够使用里面的一些方法了,比如上面使用到的filter 可以过滤掉容器里面的部分内容,count,得到最后的数量,当然还有很多方法,大家可以去看看和尝试下,感觉stream和lambda表达式一起使用挺方便的,
可以通过stream来新建一个List,
最后,增强的forEach方法,是遍历的一个方法,可以和lambda表达式一起使用,可以代替for in 循环
关于stream,我就讲到这里了,里面还有很多东西,以后再去看。
3.关于guava,
之前一直想学习使用guava的,但是发现里面的部分内容,在jdk里面直接实现了,就放弃了学习。
最近发现里面的collection这个里面的东西还挺有趣的,所以又看了下,主要是collection
(1)首先是不可变集合,jdk里面可以通过Collection.unmodifiable*方法来得到一个不可变集合,但是这个不可变集合并非是真的不可变的,对得到的集合进行操作的话,确实会抛出异常,但是如果对原集合进行操作的话,也会将得到的不可变集合的内容给改动
测试代码如下:
@Test public void testOld() { Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"})); Set<String> unmodifiableSet = Collections.unmodifiableSet(set); set.forEach(data -> { System.out.println(data); }); unmodifiableSet.forEach(data -> { System.out.println(data); }); set.remove("RED"); System.out.println("---after remove---"); set.forEach(data -> { System.out.println(data); }); unmodifiableSet.forEach(data -> { System.out.println(data); }); }
测试结果如下:
RED
GREEN
RED
GREEN
---after remove---
GREEN
GREEN
可以看到,我只将set里面的RED删除了,但是unmodifiableSet里面的内容也删除了。
看看guava里面的immutableSet:
@Test public void testGuavaImmutableSet() { Set<String> set = new HashSet<String>(Arrays.asList(new String[]{"RED", "GREEN"})); ImmutableSet<String> immutableSet = ImmutableSet.copyOf(set); set.forEach(data -> { System.out.println(data); }); immutableSet.forEach(data -> { System.out.println(data); }); set.remove("RED"); System.out.println("---after remove---"); set.forEach(data -> { System.out.println(data); }); immutableSet.forEach(data -> { System.out.println(data); }); }
测试结果如下:
RED
GREEN
RED
GREEN
---after remove---
GREEN
RED
GREEN
就没有这个问题,从方法名上也大概可以看到,jdk里面的方法,估计是将原来的set包在unmodifiableSet里面去了,但是guava是复制了一份内容保存,当然这只是猜测,没有去看源码,因为eclipse居然看不到源码,还是idea好
然后里面还有multiSet 可以插入重复值,MultiMap 一个key可以对应多个value,biMap 一个key,对应一个value,并且一个value,对应一个key,也就是一一对应。这些集合的代码就不复制出来了,不然又是全篇都是代码了。
OK,今天就写到这里把,之后几天争取再拿几张offer,争取将华为的offer拿到,加油