你们好,我是南橘,从接触java到如今也有差很少两年时间了,两年时间,从一名连java有几种数据结构都不懂超级小白,到如今懂了一点点的进阶小白,学到了很多的东西。知识越分享越值钱,我这段时间总结(包括从别的大佬那边学习,引用)了一些日常学习和面试中的重点(自我认为),但愿给你们带来一些帮助html
第一件事仍是把思惟导图贴给你们,由于用的是免费版,因此有水印,若是须要原始版本的话,能够加个人微信:java
String对象是java中重要的数据类型,在大部分状况下咱们都会用到String。在java语言漫长的进化过程当中,开发人员也对String作了大量的优化,其中字符串的不变性和常量池复用也是String的重要特色 面试
String类以final进行了修饰,在系统中就不可能有String的子类,同时String对象的状态在其被建立以后就不在发生变化。在一个对象被多线程共享,并且被频繁的访问时,能够省略同步和锁的时间,从而提升性能。它也保证 hash 属性值不会频繁变动,确保了惟一性,使得相似 HashMap 容器才能实现相应的 key-value 缓存功能。因此这一点也是出于对系统安全性的考虑。正则表达式
当两个String对象拥有同一个值的时候,他们都只是引用了常量池中的同一个拷贝。因此当程序中某个字符串频繁出现时,这个优化技术就能够节省大幅度的内存空间了。算法
你们都知道
String a ="abc";
String b ="abc";
a == b
复制代码
既然如此,那为何在String中还存在“+”之类的操做呢?数组
String经过+号来拼接字符串的时候,若是有字符串变量参与,实际上底层会转成经过StringBuilder的append( )方法来实现。缓存
咱们再继续分析"+",StringBuilder和StringBuffer的运行效率:安全
经过上面的例子咱们能够看出,使用+号拼接字符串,其效率明显低于使用StringBuffer和StringBuilder的append()方法进行拼接。同时StringBuffer的效率比StringBuilder低些,这是因为StringBuffer实现了线程安全,效率较低也是不可避免的。因此在字符串的累加操做中,建议结合线程问题选择,应避免使用+号拼接字符串。性能优化
StringBuffer和StringBuilder的是对String的封装,String是对char数组的封装。是数组就有大小,就有不够用的时候,不够用只能扩容,也就是把原来的再复制到新的数组中。合适的容量参数天然可以减小扩容的次数,达到提升效率的目的。bash
咱们在开发的过程当中应该知道,要尽可能使用toString()方法而不是使用String.valueOf()方法进行转化。why?
从这边的代码就能看出来,String.valueOf()直接调用了底层的obj.toString()方法,不过在这以前会先判断是否为空。 因此,在大多数场景,能够直接使用toString()方法就直接使用吧。
大多数状况,字符串是应用中占用内存最多的一部分。虚拟机提供了字符串池,用于存放公共的字符串。能够调用String.intern方法,返回一个字符串池中一样内容的字符串,不过这种方调用是耗时的。
JVM提供了一个新的特性,在虚拟机中添加以下参数能够开启消除重复字符串的功能:
-xx:+UseG1GC -XX:+UseStringDeduplication
JVM将尝试在垃圾收集过程当中消除重复的字符串。在垃圾收集过程当中,JVM会检查内存中全部的对象,识别重复字符串并尝试消除它。UseStringDeduplication不会消除重复的字符串对象自己,它只替换了底层的char[]。
除了以前那些比较明显的修改点,其实字符串优化中还有很多须要注意的地方。
这一点很好理解,防止变量的值为空出现空指针异常。
String对象的使用,出现字符串链接时应该使用StringBuilder/StringBuffer代替。因为Java虚拟机不只要花时间生成对象,之后可能还须要花时间对这些对象进行垃圾回收和处理,所以,生成过多的对象将会给程序的性能带来很大的影响。
原始的String.split()方法使用简单,功能强大,支持正则表达式,可是,在性能敏感的系统中频繁的使用这个方法是不可取的。咱们可使用效率更高的StringTokenizer类分割字符串。
Java中,将原始的数字类型转换为对应的Number对象的机制叫作装箱。将Number对象转化为对应原始类的机制叫作拆箱。在Java拆箱和装箱的机制是自动完成的。
int被装箱为Integer,在性能方面是要付出一些代价的,JDK为了不每次int类型装箱都须要建立一个新的Integer对象,内部使用了缓存,其代码以下:
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
复制代码
其中high的值默认是127,能够经过-XX: AutoBoxCacheMax =? 进行调整。
装箱对性能的影响不是很大,但建立过多的对象会加大垃圾回收的复旦。有不少开源工具提供了避免自动装箱的int专有集合类,好比著名的开源工具Jodd,提供了IntHashMap类、IntArrayList类。
浮点型变量在进行计算的时候会出现丢失精度的问题。
System.out.println(0.05+2.01); -->2.0599999999999996
进行商品价格计算的时候,出现这种问题每每会致使很严重的事物,好比下单的时候帐单不正确致使没法下单,或者出现对帐问题。
一般有两种办法来解决这个问题,一是用long来表示金额(以分为单位),这是效率最高的,二是使用BigDecimal来解决这类问题。
BigDecimal能保证精度,但计算会有必定的性能影响,可是差距不是特别大。因此在项目中,若是涉及精度结算,能够考虑使用BigDecimal,也可使用long。在分布式或者微服务场景中,考虑到序列化与反序列化,long也是能够被全部的序列化框架识别的。
首先把集合的继承图摆上来。
对集合的优化,更多的实际上是在适合的状况使用适合的数据结构,与字符串不一样,对于集合来讲,不一样的数据结构之间的差别是很是巨大的。
I、在知道初始值大小的状况下尽可能赋上初始值大小。
看源码就会发现,构造具备指定初始容量的空列表事实上是初始化一个空的数组列表,拿ArrayList来讲,咱们都知道它的底层是用数组进行存储的,它的默认大小是10,若是没有根据预期来设置一个初始值大小,那么它就会在使用过程当中不断地扩容(如下为扩容方法),每次扩容大小是1.5倍。
II、ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于随机访问get和set,ArrayList以为优于LinkedList,由于LinkedList要移动指针。
III、对于新增和删除操做add和remove,LinedList比较占优点,由于ArrayList要移动数据。
I、HashTable和HashMap都是基于链表+数组实现的(HashMap还有红黑树)。HashTable作了同步操做,HashMap没有,所以HashMap是线程不安全类。HashTable的key和Value是不容许存Null的。HashMap的底层是native+位运算实现的,所以效率很高。
II、HashMap是无序的,LinkedHashMap是有序的,它有2种排序方式,一种是基于存储顺序,另外一种是基于访问顺序。
III、TreeMap是基于红黑树实现的,平衡查找树查找效率优于平衡二叉树。它不一样于LinkedHashMap,它是根据key来排序的,使用TreeMap必须实现Comparable或在构造器中注入Comparator。若是须要排序,使用TreeMap的效率更高。
I、Set的特色就是不容许有重复元素,HashMap封装为HashSet、LinkedHashMap封装为LinkedHashSet、TreeMap封装为TreeSet。比起封装前的类,Set由于要进行比较,性能会比较明显的降低,因此若是不考虑去重状况通常不用Set。
I、随机访问接口,基于数组实现的如ArrayList和Vector都实现了此接口,而基于链表实现Linkedist未实现此接口,所以在进行随机访问操做时,链表的性能会相差几个数量级,是因为LinkedList在进行随机访问时须要依据元素所在位置而由前向后或从后向前遍历集合,而数组则直接经过索引标便可找到。
II、实现RandomAccess接口的集合好比ArrayList,应当使用最普通的for循环而不是foreach循环来遍历。
这是JDK中推荐给用户的。JDK的API对于RandomAccess接口的解释是:实现RandomAccess接口用来代表其支持快速随机访问,此接口的主要目的是容许通常的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验代表,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,若是是顺序访问的,则使用Iterator会效率更高。
具体状况能够参考Java语法糖1:可变长度参数以及foreach循环原理
这篇文章也是这些日子对性能调优的一些思考,参杂着《Java系统性能优化实战》这本书上第二章的内容一块儿写了出来。在平常的编码中,不少地方的代码都存在着优化的可能,这里改一点,那里修一点,不只代码会变得更漂亮,效率也会更高。
同时须要思惟导图的话,能够联系我,毕竟知识越分享越香!