java函数的编写-编写函数实现value左右循环移位
性能并不总是我们需要考虑的第一因素,但是当我们需要处理大量数据,或者对象和计算需要消耗更多资源时,性能问题自然会出现,我们需要花一些时间进行调优。 本文收集整理了 40 多个编写高性能 Java 代码的技巧,希望对你有所帮助。
清理代码并修改算法
不断重构和组织代码,去除算法中的冗余步骤,使代码更易读和精炼,通过避免额外的计算来提高性能。
尝试将类和方法指定为 final
Java 编译器会尝试内联 final 方法,从而大大提高性能(大约 50%)。 因此,如果可能的话,尽量对类和方法使用final修饰符。
避免滥用静态变量
如果没有必要,避免将成员声明为static,否则直到程序终止或引用被修改时,成员持有的对象才可能被释放。
保持方法简短
过大的方法会消耗更多的内存和 CPU 周期,而较小的方法会带来更好的内聚性、可读性和更小的内存占用,从而带来更好的性能。 尝试在适当的逻辑点将过大的方法拆分为适当的小方法。
避免使用递归
递归是一个很好的算法,有实际的好处和实际用途,但是递归本身有很高的运行成本。 如果性能是一个问题,请考虑放弃递归并改用循环。
避免滥用异常
抛出异常需要大量开销,例如创建对象和获取堆栈。 异常只是用来处理错误,并没有刻意用来控制进程的运行。
尽可能使用局部变量
调用方法传入的参数和方法创建的临时变量等局部变量都存储在栈中,比存储在堆中的静态变量和实例变量访问速度更快,并且会被快速清理随着方法调用结束。
减少变量的重复计算
调用一个方法是有一定开销的,比如创建栈帧,保护场景,还原场景等,所以如果变量只需要计算一次,可以提前计算,不用重复计算,减少方法调用。
尽可能使用基本类型
将数据存储在栈上而不是堆上,有利于更快地回收内存空间,更快地访问数据。 因此,尽量使用基本类型(如 int)而不是包装类型(Integer),以帮助减少内存消耗并提高性能。
如果可能,请尝试使用数组而不是 ArrayList。
避免 BigInteger 和 BigDecimal
BigInteger 和 BigDecimal 提供更高的计算精度,但也占用更多的内存和 CPU,如果允许,请使用 Long 和 Double 作为替代。
使用 isEmpty 检查 String 是否为空
String是字节数组,isEmpty会判断数组的长度,很快就会得到结果。
对于单个字符,使用 char 类型,而不是 String
在处理单个字符的字符串时,使用 char 可以获得更好的性能。
使用更轻量级的操作
必要时,可以用较轻的移位运算代替乘法和除法运算。
避免 Random 实例被多个线程使用
当多个线程共享一个 Random 实例时,很容易因为竞争种子而降低性能。 ThreadLocalRandom 可以用作替代方法。
使用 StringBuilder 连接字符串
使用 + 号连接字符串可能会创建过多的字符串对象。 StringBuffer有同步机制,性能不如StringBuilder,但要注意StringBuilder不是线程安全的。
使用 + 在单个语句中连接长字符串提高了可读性,并且可以由编译器进行优化。
避免在循环体中使用 + 连接字符串,这可能会导致创建过多的 StringBuilder 对象。
更好的字符串替换
在Java 8及以下版本,使用Apache Commons StringUtils.replace替换字符串比JDK原生的String.replace更高效,对于更高版本的Java,最好使用String.replace。
避免过多的 if-else 语句
在代码中过多使用if-else语句,尤其是在循环体中,会强制JVM去比较这些条件,从而消耗性能。 如果业务逻辑中判断条件过多,可以尝试对布尔结果进行分组计算,然后在if语句中使用。
尽可能重复使用现有对象,而不是随机创建它们
创建新对象是有成本的,尤其是在大量的情况下。 如果可能,尽量复用现有对象,甚至使用单例模式,而不是随意创建新对象,尤其是那些大对象。
如果有很多重复的字符串,可以考虑在创建这些字符串对象时使用String.intern方法,将字符串放入常量池中,再次创建时从常量池中获取同一个对象,减少内存占用。
只有在满足某些条件时才使用的对象可以认为是延迟到进入条件区域,而不是更早地创建。
避免在循环中声明和创建对象,创建过多的对象引用,可以将声明放在循环之前。
尝试使用 indexOf 而不是 split 来拆分字符串
在String的split方法中使用了更强大的正则表达式,但是正则表达式的性能并不总是好的,使用不当会导致回溯问题,使CPU一直居高不下。 因此,除非回溯问题可控,否则尽量使用indexOf拆分字符串。
每当你使用正则表达式时,尽量使用独占模式,避免使用贪婪模式,避免分支选择,避免捕获组的嵌套等,以避免性能问题。
选择正确的集合类型
ArrayList 和 HashMap 不是线程安全的,而 Vector 和 Hashtable 是线程安全的同步集合。 当在没有线程同步的多线程环境中可能需要线程安全的集合时,尝试使用ArrayList和HashMap以获得多方面的性能提升。
ArrayList和LinkedList分别基于数组和链表实现。 两者迭代器遍历(包括foreach)的性能不相上下。 但是在使用for循环遍历的时候,ArrayList由于其随机存取快的特点,比LinkedList的性能要高。 另外,ArrayList在不扩容的情况下,在末尾增删元素的性能略高于LinkedList,而在头部添加元素的性能高于ArrayList。
在构造时初始化集合
如果集合中的元素在初始化的时候就可以确定,那么尽量在构造集合对象的时候就初始化,而不是先实例化集合对象,然后一个一个添加元素。
HashMap的高效使用
当数据量已知时,提前设置初始容量(数据量÷负载因子),避免扩容开销。
通常,默认的加载因子 (0.75) 就足够了。 当特别需要充分利用内存资源时,可以增加负载因子。 当查询操作频繁时,可以降低负载率。
不要在循环中获取集合的大小
如果要遍历集合,那么就提前获取集合的长度,而不是每次都在循环中判断集合的大小。
尽量减少对收集方法的调用次数
集合提供了很多有用的方法,比如size()、containsKey()等,如果可能,尽量减少和避免调用这些方法
使用 addAll 而不是添加
addAll 可以笔加具有更高的每秒操作数。 如果可能,在向ArrayList等集合中添加多个元素时,尽量使用addAll分批添加,而不是通过add方法一个一个添加。
使用 entrySet 而不是 keySet
EntrySet在一秒内可以比KeySet多运行9000次操作,所以在遍历HashMap时尽量使用entrySet而不是keySet。
使用singletonList构造单元素集合
使用 singletonList 生成单元素集合比使用构造函数 new 更好。
使用 EnumSet 而不是 HashSet
如果枚举值(Enum)存储在Set中,最好使用EnumSet以获得更好的性能。
慎用ArrayList的contains方法
大多数集合都有一个 contains 方法,用于确定某个元素是否已存在于集合中。 但是,当ArrayList或Vector等集合中的数据量很大时,这种方法在最坏的情况下会遍历整个集合。 如果在循环中使用,会带来巨大的性能开销。 因此java函数的编写,当需要在大数据集中进行搜索时,可以考虑使用HashMap,而不是ArrayList。
必要时使用 Stream 遍历集合
一般来说,集合的定期迭代对于小数量、低循环次数和在单核上运行的应用程序来说性能更高。 如果在多核环境下迭代大数据集,可以考虑使用具有并行机制的 Stream。
必要时使用 NIO
传统的I/O类在高并发和大数据场景下容易阻塞,内核空间和用户空间的数据复制也有性能开销。 一般场景可以使用Buffer来解决阻塞问题。 当性能要求更高时,需要引入NIO,通过DirectBuffer、Channel、多路复用来提升性能。
必要时使用高性能通信协议
通常,在对性能要求较高的分布式环境中,应用程序间的服务调用会使用RPC通信框架,而RMI这种较早的RPC通信方式java函数的编写,由于序列化性能差、I/O阻塞、短小等问题导致性能较差连接。 不能满足需要。 为此,可以改用Dubbo这种全方面优化的通信协议来满足高并发和小对象传输的需求。
必要时使用序列化框架
Java 的原生序列化性能很差。 如果需要,可以使用Protobuf、FastJson、Kryo等序列化框架进行安全高效的序列化。
谨慎使用字符串作为同步对象
JVM 将缓存字符串对象。 如果不使用 new String,在不同地方声明的字符串可能指向同一个字符串对象。 如果将其用作同步对象,可能会出现意想不到的后果。 此外,作为一般规则,尽量将同步块内的处理量保持在最低限度以提高性能。
多线程调优技术
对于在多线程上运行的代码,请考虑以下调优方法:
当逻辑简单,运行速度快时,优先考虑单线程模式。 对于耗时复杂的计算,可以考虑多线程并发,降低锁的粒度,减少同步块,分离读写锁,尽量减少锁的持有时间。 减少锁竞争,提高自旋锁的成功率,降低升级为重量级锁的可能性。 当只需要可见性和顺序性时,可以使用开销较小的volatile关键字来避免上下文切换,并设置合理的线程池。 大小,避免过多线程的上下文切换开销 及时使用并发容器类。 当对数据有强一致性要求时,可以使用Hashtable、Vector等强一致性容器。 当读取远大于写入时,可以使用CopyOnWriteArrayList。 当数据量大时 尺寸小且查询频繁时,可以使用ConcurrentHashMap容器类。输出前检查日志级别
输出某个级别的日志前,先检查该级别是否开启,避免额外计算。
避免向日志输出大对象
输出对象到日志时,只选择关键属性输出,避免序列化整个大对象输出。
缓存昂贵的资源
创建对象有性能开销。 有些对象(比如数据库连接)的创建过程特别耗时,而有些对象本身占用的资源也比较大。 缓存这些对象以供重用而不是随机创建它们将大大提高性能。
如果可能,尽量使用像 Integer.valueOf 这样的方法重用内存中已有的对象,而不是用 new Integer 创建新的对象,尤其是当需要大量这样的对象时。
但需要注意的是,缓存需要额外的管理,只有在成本效益高的情况下才会使用。
使用预编译的 SQL 语句
预处理语句(PreparedStatement)具有一次编译多次使用的特点,比普通Statement具有更好的性能。 准备好的语句对于 SQL 注入也是安全的。
只选择需要的数据列而不是 *
在 SELECT 语句中,只返回那些需要的列,这样可以节省网络带宽并带来更好的性能。
使用正确的表连接
当需要关联查询多个表的数据时,注意使用正确的表连接方式,并在连接字段上建立必要的索引。
使用连接而不是子查询
与只遍历数据一次的join相比,子查询多次遍历数据,比join耗时更多。
使用存储过程而不是查询
如果查询语句过于复杂和冗长,可以考虑改用数据库存储过程,以获得存储过程预编译、较少数据传递等带来的性能。