Share a few practical code optimization tips at work!

foreword

I shared an article on code optimization before: optimization of multi-level nested problems of conditional statements, helping you write code that won’t let colleagues complain!

Today, I will share some code optimization techniques that I commonly use in my daily work. I hope it will be helpful to you!

The article was first published on the public account (Moon with Feiyu), and then synchronized to the personal website: xiaoflyfish.cn/

I feel that I have gained something, I hope to help you like it, forward it, thank you, thank you

text

Minimize visibility of class members and methods

Example: If it is a private method, delete it if you want to delete it

If a public service method, or a public member variable, delete it, don't think too much.

Use shift operations instead of multiplication and division

Computers are represented using binary, and bit-shift operations can greatly improve performance.

<< left shift is equivalent to multiplying by 2; >> right shift is equivalent to dividing by 2;

>>> An unsigned right shift is equivalent to dividing by 2, but it ignores the sign bit and padded with zeros.

a = val << 3;
b = val >> 1;
复制代码

Minimize double counting of variables

We know that calls to methods are expensive, including creating stack frames, protecting the scene when calling methods, restoring the scene, and so on.

//反例
for (int i = 0; i < list.size(); i++) {
  System.out.println("result");
}

//正例
for (int i = 0, length = list.size(); i < length; i++) {
  System.out.println("result");
}
复制代码

When list.size()it is very large, it reduces a lot of consumption.

Don't catch RuntimeException

RuntimeException should not be caught by catch statement, but should be avoided by coding.

As in the following code, list may have an array out of bounds exception.

Whether it is out of bounds can be judged in advance through the code, rather than waiting for an exception to be caught.

Judging this way in advance, the code will be more elegant and more efficient.

public String test1(List<String> list, int index) {
    try {
        return list.get(index);
    } catch (IndexOutOfBoundsException ex) {
        return null;
    }
}

//正例
public String test2(List<String> list, int index) {
    if (index >= list.size() || index < 0) {
        return null;
    }
    return list.get(index);
}
复制代码

Use local variables to avoid heap allocations

Since the heap resource is shared by multiple threads, it is the main area for the garbage collector to work. Too many objects will cause GC pressure. Variables can be allocated on the stack by means of local variables. In this way, the variables will be destroyed when the method is executed, which can reduce the pressure on the GC.

reduce the scope of the variable

Pay attention to the scope of variables and minimize object creation.

As in the code below, the variable s is created every time the method is entered and can be moved inside the if statement.

public void test(String str) {
    final int s = 100;
    if (!StringUtils.isEmpty(str)) {
        int result = s * s;
    }
}
复制代码

Try to use a lazy loading strategy and create it when needed

String str = "月伴飞鱼";
if (name == "公众号") {
  list.add(str);
}

if (name == "公众号") {
  String str = "月伴飞鱼";
  list.add(str);
}
复制代码

Access static variables directly using the class name

使用对象访问静态变量,这种方式多了一步寻址操作,需要先找到变量对应的类,再找到类对应的变量。

 // 反例
int i = objectA.staticMethod();
 // 正例
int i = ClassA.staticMethod();
复制代码

字符串拼接使用StringBuilder

字符串拼接,使用 StringBuilder 或者 StringBuffer,不要使用 + 号。

//反例
public class StringTest {
    @Test
    public void testStringPlus() {
        String str = "111";
        str += "222";
        str += "333";
        System.out.println(str);
    }
     
}

//正例
public class TestMain {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("111");
        sb.append("222");
        sb.append(333);
        System.out.println(sb.toString());
    }
}
复制代码

重写对象的HashCode,不要简单地返回固定值

有同学在开发重写 HashCode 和 Equals 方法时,会把 HashCode 的值返回固定的 0,而这样做是不恰当的

当这些对象存入 HashMap 时,性能就会非常低,因为 HashMap 是通过 HashCode 定位到 Hash 槽,有冲突的时候,才会使用链表或者红黑树组织节点,固定地返回 0,相当于把 Hash 寻址功能无效了。

HashMap等集合初始化的时候,指定初始值大小

这样的对象有很多,比如 ArrayList,StringBuilder 等,通过指定初始值大小可减少扩容造成的性能损耗。

初始值大小计算可以参考《阿里巴巴开发手册》:

picture

循环内不要不断创建对象引用

//反例
for (int i = 1; i <= size; i++) {
    Object obj = new Object();    
}

//正例
Object obj = null;
for (int i = 0; i <= size; i++) {
    obj = new Object();
}
复制代码

第一种会导致内存中有size个Object对象引用存在,size很大的话,就耗费内存了

遍历Map 的时候,使用 EntrySet 方法

使用 EntrySet 方法,可以直接返回 set 对象,直接拿来用即可;而使用 KeySet 方法,获得的是key 的集合,需要再进行一次 get 操作,多了一个操作步骤,所以更推荐使用 EntrySet 方式遍历 Map。

Set<Map.Entry<String, String>> entryseSet = nmap.entrySet();
for (Map.Entry<String, String> entry : entryseSet) {
    System.out.println(entry.getKey()+","+entry.getValue());
}
复制代码

不要在多线程下使用同一个 Random

Random 类的 seed 会在并发访问的情况下发生竞争,造成性能降低,建议在多线程环境下使用 ThreadLocalRandom 类。

 public static void main(String[] args) {
        ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
        Thread thread1 = new Thread(()->{
            for (int i=0;i<10;i++){
                System.out.println("Thread1:"+threadLocalRandom.nextInt(10));
            }
        });
        Thread thread2 = new Thread(()->{
            for (int i=0;i<10;i++){
                System.out.println("Thread2:"+threadLocalRandom.nextInt(10));
            }
        });
        thread1.start();
        thread2.start();
    }
复制代码

自增推荐使用LongAddr

自增运算可以通过 synchronized 和 volatile 的组合来控制线程安全,或者也可以使用原子类(比如 AtomicLong)。

The speed of the latter is higher than the former. AtomicLong uses CAS for comparison and replacement, which will cause too many invalid spins in the case of many threads. LongAdder can be used to replace AtomicLong for further performance improvement.

public class Test {
    public int longAdderTest(Blackhole blackhole) throws InterruptedException {
        LongAdder longAdder = new LongAdder();
        for (int i = 0; i < 1024; i++) {
            longAdder.add(1);
        }
        return longAdder.intValue();
    }
}
复制代码

Use less reflection in your program

The function of reflection is very powerful, but it is realized by parsing bytecode, and the performance is not very ideal.

In reality, there are many optimization methods for reflection, such as caching the process of reflection execution (such as Method), and using multiplexing to speed up reflection.

After Java 7.0, a new package java.lang.invokewas added, and a new JVM bytecode instruction invokedynamic was added to support calling the target method directly through a string from the JVM level.

At last

I feel that I have gained something, I hope to help you like it, forward it, thank you, thank you

WeChat search: Flying Fish on the Moon, make friends, join the interview exchange group

Guess you like

Origin juejin.im/post/7086461795308666894