Java performance optimization - performance comparison of test arrays and linked lists when querying and adding and deleting

Scenes

Use JMH (Java Microbenchmark Harness micro-benchmark testing framework) in Java for performance testing and optimization:

Use JMH (Java Microbenchmark Harness micro-benchmark testing framework) in Java for performance testing and optimization - Programmer Sought

When using JMH above, we tested the comparison results of arrays and linked lists in Java when inserting headers.

The following compares the performance comparison of querying and adding/deleting at the head, middle, and tail respectively, because the process of adding and deleting is similar,

So just test the addition.

Arrays and Linked Lists in Java

array

Array (Array) is a data structure composed of a collection of elements of the same type, which allocates a piece of continuous memory for storage.

The storage address corresponding to the element can be calculated by using the index of the element.

The "continuous" feature of the array determines that its access speed is very fast, because it is stored continuously, so this determines that its storage location is fixed,

So its access speed is very fast.

The disadvantage is that it has relatively high requirements for memory, and a piece of continuous memory must be found.

Another disadvantage of arrays is that the efficiency of insertion and deletion is relatively slow. If we insert or delete a piece of data at the non-tail of the array, then the

To move all the data afterwards, this will bring a certain performance overhead. The array also has a disadvantage that its size is fixed and cannot be dynamically expanded.

linked list

Linked list (Linked list) is a common basic data structure. It is a linear list, but it does not store data in a linear order.

Instead, a pointer (Pointer) to the next node is stored in each node. Since it does not have to be stored in order, the linked list can be inserted

To achieve the complexity of O(1), it is much faster than another linear list sequence table, but looking up a node or accessing a node with a specific number is much faster

It takes O(n) time, while the corresponding time complexities of sequential tables are O(logn) and O(1) respectively.

A linked list is a data structure that does not require continuous memory storage. The elements of the linked list have two attributes, one is the value of the element, and the other is a pointer.

This pointer marks the address of the next element.

Linked lists are mainly divided into the following categories:

singly linked list

The singly linked list contains two fields, an information field and a pointer field. This link points to the next node in the list, and the last node points to a null value.

Doubly linked list

A doubly linked list is also called a doubly linked list. There are not only pointers to the next node in the doubly linked list, but also pointers to the previous node, so that

From any node to visit the previous node, of course, you can also visit the next node, and even the entire linked list.

circular linked list

The first node in the circular linked list is the last node, and vice versa.

Why are there single and double linked lists?

This starts with the deletion of the linked list. If the one-way linked list needs to delete elements, not only the deleted node must be found, but also the deleted node must be found.

The previous node of the point (usually called the predecessor), because it is necessary to change the next pointer in the previous node, but also because it is a one-way linked list,

Therefore, the deleted node does not store the relevant information of the previous node, so we need to query the linked list again to find the previous node.

This brings certain performance problems, so there is a doubly linked list.

Advantages of linked list:

1. The memory utilization rate of the linked list is relatively high, no continuous memory space is required, and even if there is memory fragmentation, the creation of the linked list will not be affected;

2. The insertion and deletion speed of the linked list is very fast, and there is no need to move a large number of elements like an array;

3. The size of the linked list is not fixed, and it can be easily expanded dynamically.

Linked List Disadvantages

The main disadvantage of the linked list is that it cannot be searched randomly, it must be traversed from the first one, the search efficiency is relatively low, and the time complexity of linked list query is O(n).

In the Java language, the representative of an array is ArrayList, and the representative of a linked list is LinkedList, so we use these two objects for testing.

Note:

Blog:
Overbearing rogue temperament blog_CSDN Blog-C#, Architecture Road, Blogger in SpringBoot

accomplish

1. Add test head

import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.concurrent.TimeUnit;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 2,time = 1,timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5,time = 5,timeUnit = TimeUnit.SECONDS)
@Fork(1)
@State(Scope.Thread)
//头部添加性能测试
public class ArrayListWithLinkedListTestHeaderAdd {

    private static final int maxSize = 10000; //测试循环次数
    private static final int operationSize = 100; //操作次数

    private static ArrayList<Integer> arrayList;
    private static LinkedList<Integer> linkedList;

    public static void main(String[] args) throws RunnerException {
        //启动基准测试
        Options opt = new OptionsBuilder()
                .include(ArrayListWithLinkedListTestHeaderAdd.class.getSimpleName())  //要导入的测试类
                .build();
        //执行测试
        new Runner(opt).run();
    }

    //@Setup作用于方法上,用于测试前的初始化工作
    //启动执行事件
    @Setup
    public void init(){
        arrayList = new ArrayList<Integer>();
        linkedList = new LinkedList<Integer>();
        for (int i = 0; i < maxSize; i++) {
            arrayList.add(i);
            linkedList.add(i);
        }
    }

    //用于回收某些资源
    @TearDown
    public void finish(){

    }

    @Benchmark
    public void addArrayByFirst(Blackhole blackhole){
        for (int i = 0; i < operationSize; i++) {
            arrayList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByFirst(Blackhole blackhole){
        for (int i = 0; i < operationSize; i++) {
            linkedList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(linkedList);
    }
}

The test result output is as follows:

Benchmark                                              Mode  Cnt        Score         Error  Units
ArrayListWithLinkedListTestHeaderAdd.addArrayByFirst   avgt    5  6597432.461 ± 8384921.207  ns/op
ArrayListWithLinkedListTestHeaderAdd.addLinkedByFirst  avgt    5   111880.375 ±  258245.983  ns/op

It can be seen that the average completion time of LinkedList is about 60 times faster than the average completion time of ArrayList when inserting the head

2. Add in the middle of the test

    @Benchmark
    public void addArrayByMiddle(Blackhole blackhole){
        int startIndex = maxSize /2 ;//获取中间值
        for (int i = startIndex; i < (startIndex + operationSize); i++) {
            arrayList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByMiddle(Blackhole blackhole){
        int startIndex = maxSize /2 ;//获取中间值
        for (int i = startIndex; i < (startIndex + operationSize); i++) {
            linkedList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(linkedList);
    }

Test Results

Benchmark                                              Mode  Cnt        Score         Error  Units
ArrayListWithLinkedListTestMiddleAdd.addArrayByMiddle   avgt    5  6703786.087 ± 9225973.854  ns/op
ArrayListWithLinkedListTestMiddleAdd.addLinkedByMiddle  avgt    5  1064823.250 ±  697306.502  ns/op

It can be seen that the average completion time of LinkedList is about 7 times faster than the average completion time of ArrayList during intermediate insertion

3. Add at the end of the test

    @Benchmark
    public void addArrayByTail(Blackhole blackhole){
        int startIndex = maxSize - 1 - operationSize ;
        for (int i = startIndex; i < (maxSize - 1); i++) {
            arrayList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(arrayList);
    }

    @Benchmark
    public void addLinkedByTail(Blackhole blackhole){
        int startIndex = maxSize - 1 - operationSize ;
        for (int i = startIndex; i < (maxSize - 1); i++) {
            linkedList.add(i,i);
        }
        //为了避免JIT忽略未被使用的结果计算/为了避免死码消除问题
        blackhole.consume(linkedList);
    }

Test Results

Benchmark                                            Mode  Cnt        Score          Error  Units
ArrayListWithLinkedListTestTailAdd.addArrayByTail   avgt    5  6981011.770 ± 10173306.725  ns/op
ArrayListWithLinkedListTestTailAdd.addLinkedByTail  avgt    5  2180224.087 ±   913796.230  ns/op

It can be seen that the average completion time of LinkedList is about 3 times faster than the average completion time of ArrayList when performing tail insertion

4. Test header query

    @Benchmark
    public void searchArrayByFirst(){
        for (int i = 0; i < operationSize; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void searchLinkedByFirst(){
        for (int i = 0; i < operationSize; i++) {
            linkedList.get(i);
        }
    }

Test Results

Benchmark                                                    Mode  Cnt     Score      Error  Units
ArrayListWithLinkedListTestHeaderSearch.searchArrayByFirst   avgt    5     3.578 ±    0.835  ns/op
ArrayListWithLinkedListTestHeaderSearch.searchLinkedByFirst  avgt    5  4232.832 ± 2019.900  ns/op

5. Query in the middle of the test

    @Benchmark
    public void searchArrayByMiddle(){
        int startIndex = maxSize / 2;
        int endIndex = startIndex + operationSize;
        for (int i = startIndex; i < endIndex; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void searchLinkedByMiddle(){
        int startIndex = maxSize / 2;
        int endIndex = startIndex + operationSize;
        for (int i = startIndex; i < endIndex; i++) {
            linkedList.get(i);
        }
    }

Test Results

Benchmark                                                     Mode  Cnt        Score        Error  Units
ArrayListWithLinkedListTestMiddleSearch.searchArrayByMiddle   avgt    5        4.969 ±      6.568  ns/op
ArrayListWithLinkedListTestMiddleSearch.searchLinkedByMiddle  avgt    5  1131641.772 ± 313408.389  ns/op

It can be seen that the average completion time of ArrayList is about 230947 times faster than the average completion time of LinkedList when performing central queries

6. Test tail query

    @Benchmark
    public void searchArrayByTail(){
        for (int i = maxSize - operationSize; i < maxSize; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void searchLinkedByTail(){
        for (int i = maxSize - operationSize; i < maxSize; i++) {
            linkedList.get(i);
        }
    }

Test Results

Benchmark                                                 Mode  Cnt     Score     Error  Units
ArrayListWithLinkedListTestTailSearch.searchArrayByTail   avgt    5     5.074 ±   5.384  ns/op
ArrayListWithLinkedListTestTailSearch.searchLinkedByTail  avgt    5  5689.329 ± 563.806  ns/op

It can be seen that the average completion time of ArrayList is about 1100 times faster than the average completion time of LinkedList when performing tail queries

7. Summary: Is the linked list in Java more than 1000 times slower than the array?

It can be seen that the average completion time of ArrayList is about 1410 times faster than the average completion time of LinkedList when performing head query

We can see from the final evaluation that after the data initialization is completed, when we perform the insert operation, especially when inserting from the head,

Because the array has to move all the elements after it, the performance is much lower than that of the linked list; but the performance is just the opposite when querying, because the linked list needs to traverse the query,

And LinkedList is a doubly linked list, so the performance of the intermediate query is tens of thousands of times slower than that of the array query (query 100 elements),

When querying at both ends (head and tail), the linked list is also nearly 1000 times slower than the array (query 100 elements).

Therefore, in scenarios where there are many queries, we should try to use arrays, and when there are many addition and deletion operations, we should use a linked list structure.

Guess you like

Origin blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/131750105