标签:读取 次数 轻量级 需要 遍历 静态方法 一起 dHash nal
一. 字符串优化处理
1. String对象组成:char数组,offset偏移量,count长度;
2. String对象特点:
不变性:String对象一旦生成,则不能再对它进行改变;
针对常量池的优化:当两个String对象拥有相同的值时,他们只引用常量池中的同一个拷贝;
类的final定义;
3. subString()
subString()方法之所以会引起内存泄漏,是因为调用了String(int offset, int count, char value[])构造函数,此构造函数采用了用空间换时间的手段,截取字符串时原生内容被复制到了新的字符串中;
4. 字符串的分割与查找
String.split()方法使用简单,功能强大,但是在性能敏感的系统中频繁使用这个方法是不可取的;
StringTokenizer性能优于split()方法;
StringTokenizer st = new StringTokenizer(orgStr, ";"); for(int i = 0; i < 10000; i ++) { while (st.hasMoreTokens()) { st.nextToken(); } st = new StringTokenizer(orgStr, ";") }
使用indexOf()和subSting()实现的分割算法性能最好,可读性和可维护性最差;
String tmp = orgStr; for(int i = 0; i < 10000; i ++) { while(true) { String splitStr = null; int j = tem.indexOf(‘;‘); if(j < 0) break; splitStr = tmp.subString(0, j); tmp = tmp.subString(j + 1); } tmp = orgStr; }
5. charAt()
orgStr.charAt(0) == ‘a‘ && orgStr.charAt(0) == ‘b‘ && orgStr.charAt(0) == ‘c‘ 效率高于orgStr.startsWith("abc");
6. StringBuffer和StringBuilder
对于String常量的累加操作,在编译时会进行优化,将多个连接操作的字符串在编译时合成一个单独的长字符串,并不会生成多个String实例;
对于String变量的累加操作,也做了相应的优化操作,使用了StringBuilder对象来实现字符串的累加;
对于String操作,类似于“+”和“+=”的运算符应该尽量少用,concat()方法的效率远远高于“+”和“+=”运算符,但又低于StringBuilder类;
StringBuilder的效率高于StringBuffer,但无法保证线程安全;
StringBuilder/StringBuffer的默认容量是16字节,超出容量需要扩容,扩容策略是将原有的容量大小翻倍,申请新的内存空间,将原数组中的内容复制到新数组,所以如果能预估StringBuilder/StringBuffer的大小,将能够有效的节省这些操作;
二. 核心数据结构
1. List接口
ArrayList和Vector使用了数组实现,初始值大小为10,每次扩容为1.5倍,唯一区别是Vector是线程安全的;
LinkedList使用了循环双向链表的数据结构;
增加元素到列表尾端:因为数组的连续性,总是在尾端增加元素时,只有在空间不足时才产生数组扩容和数组复制,大部分的追加操作效率非常高,LinkedList每次增加元素都需要新建一个Entry对象,并进行更多的赋值操作,对性能有一定的影响;
增加元素到列表任意位置:插入的元素在List中的位置越靠前,数组重组的开销就越大,可以考虑用LinkedList代替ArrayList;
删除任意位置元素:在ArrayList的每一次删除操作后都需要进行数组的重组,位置越靠前,开销越大;对于LinkedList,双向链表在查找要删除的元素时效率很高,但是移除中间元素则几乎要遍历半个List,在数据量很大时效率很低;所以在删除头或尾部的数据时,可以采用LinkedList,移除中间的元素时使用ArrayList;
对于LinkedList进行随机访问时,总会进行一次列表的遍历操作,所以使用for循环遍历的性能是很差的;forEach在编译后比迭代器多了一步赋值操作,导致性能略差一些;
2. Map接口
HashMap是将key做hash算法,将hash值映射到内存地址,直接取得key对应的数据;底层采用数组实现,内存地址即数组的下标索引;
LinkedHashMap是一个维护了元素次序表的HashMap,可以提供元素插入时的顺序或最近访问的顺序两种实现;
所有的集合都不允许在迭代器模式中修改集合的机构,否则会抛出ConcurrentModificationException,LinkedHashMap在根据元素对吼访问时间进行排序的模式中,没当使用get()方法访问某一元素时,该元素便被移动到链表的尾端,所以此时不能在迭代器中使用get()操作;
TreeMap内部基于红黑树实现,可以根据key进行排序;
3. Set接口
所有Set的实现,都只是对应的Map的一种封装而已;
三. 使用NIO提升性能
1. Channel是一个双向通道,既可读,也可写,但不能直接对Channel进行读写操作,必须通过Buffer来进行;
2. Buffer中有3个重要参数:位置position,容量capacity,上限limit;
3. MappedByteBuffer可以大大提高读取和写入文件的速度,可以适当使用这种方式;
4. 在对普通的ByteBuffer访问时,系统总是会使用一个“内核缓冲区”进行间接的操作,而DirectBuffer所处的位置就相当于这个“内核缓冲区”,由于是更加接近系统底层的方法,所以速度更快;
5. 创建和销毁DirectBuffer的成本较高,所以在频繁创建Buffer的场合不适用,如果可以复用DirectBuffer,可以大幅改善性能;
四. 引用类型
强引用:直接访问目标对象;不会被系统回收;可能导致内存泄漏;
软引用:当堆使用率接近阈值时才会被JVM回收,可以用于实现对内存敏感的Cache;
弱引用:在系统GC时,只有发现弱引用,便会进行回收;软引用,弱引用都可用于保存可有可无的缓存数据,内存不足时可以被回收,内存充足时用来提高访问速度;
虚引用:必须和引用队列一起使用,它的作用在与跟踪垃圾系统回收过程;
WeakHashMap使用弱引用,可以用作缓存,可以自动释放已经被回收的key所在的表项,但如果key都在系统内持有强引用,那么WeakHashMap就退化为普通的HashMap;
五. 有助于改善性能的技巧
1. try-catch语句对系统性能而言是非常糟糕的,尤其放在循环体内;
2. 调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快,其他变量,如静态变量,实例变量等,都在堆(Heap)中创建,速度较慢;
3. 尝试使用位运算代替部分算数运算,最典型的就是对于整数的乘除运算优化;
4. 使用数组或枚举类代替Switch语句;
5. 一维数组代替二维数组;
6. 提取表达式,减少重复劳动,尤其是循环体中;
7. 展开循环,减少循环次数,提升系统性能;
int[] array = new int[9999999]; for (int i = 0; i < 9999999; i ++) { array[i] = i; // 赋值操作 } for (int i = 0; i < 9999999; i += 3) { // 展开循环,一个循环体完成3个循环的工作 array[i] = i; array[i+1] = i + 1; array[i+2] = i + 2; }
8. 数组复制,System.arrayCopy()函数是native函数,通常native函数的性能要优于普通的函数;
9. 使用new关键字创建轻量级对象时,速度非常快,但是对于重量级对象,由于对象在构造函数中可能会进行一些复杂且耗时的操作,因此构造函数的执行时间可能会比较长,Object.clone()方法可以绕过对象的构造函数,快速复制一个对象实例,默认情况下,生成的实例只是原对象的浅拷贝,如果需要深拷贝,需要重新实现clone()方法;
10. 由于实例方法需要维护一张类似虚函数表的结构,以实现对多态的支持,与静态方法比,实例方法的调用需要更多的资源,对于一些常用的工具类方法,没有对其进行重载的必要,可以声明为static,调用时也不需要生成类的实例,比调用实例方法更为方便,易用;
标签:读取 次数 轻量级 需要 遍历 静态方法 一起 dHash nal
原文地址:https://www.cnblogs.com/bbbbs/p/12588275.html