[Data Structure] Sequence List and ArrayList

Author's homepage: paper jie's blog

Author of this article: Hello everyone, I am paper jie. Thank you for reading this article. Welcome to Yijiansanlian.

This article is included in the "JAVA Data Structure" column. This column is carefully created for college students and programming novices. The author spent a lot of money (time and energy) to build it to cover all the basic knowledge of javaSE. I hope it can help readers.

Other columns: "Detailed Explanation of Algorithms", "C Language", "javaSE", etc.

Content sharing: This issue will explain the sequence table in the data structure.

Table of contents

linear table

Sequence table

Simulation implementation of simple sequence table

collection framework

Introduction to ArrayList

Use of ArrayList

ArrayList construction

Basic methods of ArrayList

Traversal of ArrayList

ArrayList expansion mechanism

Drawing analysis

Specific use of ArrayList

Advantages and disadvantages of sequence table ArrayList storage structure


linear table

A linear list is a finite sequence of multiple data elements with the same attributes. Linear list is a data structure that can be widely used in our work. Common linear lists: sequential list, linked list, stack, queue...

A linear table is logically a linear structure, a continuous straight line. But it may not be continuous in physical structure. When linear tables are physically stored, they are generally stored in arrays and linked structures.

Sequence table

A sequence table is a linear structure that uses a storage unit with a continuous physical address to store data elements in sequence. Generally, array storage is used. Complete the addition, deletion, checking and modification of data on the array.

Simulation implementation of simple sequence table

You can go to my Gitee to see the code. There are a lot of them, so I won’t show them here: structure_code/java2023.9.12/src/mylist · Peng Zijie/Data Structure-Code Cloud-Open Source China (gitee.com)

collection framework

The java collection framework, also called a container, is a set of interfaces and its implementation classes defined under the java.util package. Its main function is to place multiple elements into a unit for fast and convenient storage, retrieval, and manipulation of these elements, which is what we usually call additions, deletions, and modifications.

Overview of classes and interfaces:

Introduction to ArrayList

In Java's collection framework (container), ArrayList is a sequence list. It is an ordinary class that implements the List interface. The framework is as follows:

Notice:

ArrayList is implemented in a generic manner and needs to be instantiated before use.

ArrayList implements the RandomAccess interface, indicating that ArrayList supports random access

ArrayList implements the cloneable interface, indicating that ArrayList can be cloned

ArrayList implements the Serializable interface, indicating that ArrayList supports serialization

Unlike Vector, ArrayList is not thread-safe and can be used in single threads. In multi-threads, you can choose Vector or CopyOnWriteArrayList.

The bottom layer of ArrayList is a continuous space and can be dynamically expanded. It is a dynamically typed sequence list.

Use of ArrayList

ArrayList construction

public class Test {

    public static void main(String[] args) {
// ArrayList创建,推荐写法
// 构造一个空的列表
        List<Integer> list1 = new ArrayList<>();
// 构造一个具有10个容量的列表
        List<Integer> list2 = new ArrayList<>(10);
        list2.add(1);
        list2.add(2);
        list2.add(3);
// list2.add("hello"); // 编译失败,List<Integer>已经限定了,list2中只能存储整形元素
// list3构造好之后,与list中的元素一致
        ArrayList<Integer> list3 = new ArrayList<>(list2);
// 避免省略类型,否则:任意类型的元素都可以存放,使用时将是一场灾难
        List list4 = new ArrayList();
        list4.add("111");
        list4.add(100);
    }
}

Basic methods of ArrayList

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("JavaSE");
        list.add("JavaWeb");
        list.add("JavaEE");
        list.add("JVM");
        list.add("测试课程");
        System.out.println(list);
// 获取list中有效元素个数
        System.out.println(list.size());
// 获取和设置index位置上的元素,注意index必须介于[0, size)间
        System.out.println(list.get(1));
        list.set(1, "JavaWEB");
        System.out.println(list.get(1));
// 在list的index位置插入指定元素,index及后续的元素统一往后搬移一个位置
        list.add(1, "Java数据结构");
        System.out.println(list);
// 删除指定元素,找到了就删除,该元素之后的元素统一往前搬移一个位置
        list.remove("JVM");
        System.out.println(list);
// 删除list中index位置上的元素,注意index不要超过list中有效元素个数,否则会抛出下标越界异常
        list.remove(list.size()-1);
        System.out.println(list);
    } // 查找指定元素第一次出现的位置:indexOf从前往后找,lastIndexOf从后往前找
        list.add("JavaSE");
        System.out.println(list.indexOf("JavaSE"));
        System.out.println(list.lastIndexOf("JavaSE"));
        // 使用list中[0, 4)之间的元素构成一个新的SubList返回,但是和ArrayList共用一个elementData数组
        // List<String> ret = list.subList(0, 4);
        System.out.println(ret);
        list.clear();
        System.out.println(list.size());
}

Traversal of ArrayList

ArrayList can be traversed in three ways: for loop, foreach, and iterator

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
// 使用下标+for遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        } System.out.println();
// 借助foreach遍历
        for (Integer integer : list) {
            System.out.print(integer + " ");
        } System.out.println();
// 使用迭代器遍历        
        Iterator<Integer> it = list.listIterator();
        while(it.hasNext()){
            System.out.print(it.next() + " ");
        } System.out.println();
    }

The iterator here is a type of design pattern.

ArrayList expansion mechanism

 First, let’s look at a piece of code:

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            list.add(i);
        }
    }

We know that ArrayList is a dynamically typed sequence list, that is, it will automatically expand during the process of inserting elements. We can take a look at the source code:

Object[] elementData; // 存放元素的空间
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认空间
private static final int DEFAULT_CAPACITY = 10; // 默认容量大小
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
} r
eturn minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// 获取旧空间大小
int oldCapacity = elementData.length;
// 预计按照1.5倍方式扩容
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
if (newCapacity - minCapacity < 0) 
newCapacity = minCapacity;
// 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 调用copyOf扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
// 如果minCapacity小于0,抛出OutOfMemoryError异常
if (minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

Drawing analysis

【Summarize】

Check whether expansion is really needed. If g calls row, prepare for expansion.

Estimate the required storage capacity

Initially expanded by 1.5 times the size

If the size required by the user exceeds 1.5 times, the capacity will be expanded according to the size required by the user.

Before the actual expansion, check whether the expansion can be successful to prevent the expansion from being too large and causing the expansion to fail.

Use copyof for expansion

Specific use of ArrayList

Here is a simple shuffling algorithm:

public class Card {
public int rank; // 牌面值
public String suit; // 花色
@Override
public String toString() {
return String.format("[%s %d]", suit, rank);
}
}

import java.util.List;
import java.util.ArrayList;
import java.util.Random;
public class CardDemo {
public static final String[] SUITS = {"♠", "♥", "♣", "♦"};
// 买一副牌
private static List<Card> buyDeck() {
List<Card> deck = new ArrayList<>(52);
for (int i = 0; i < 4; i++) {
for (int j = 1; j <= 13; j++) {
String suit = SUITS[i];
int rank = j;
Card card = new Card();
card.rank = rank;
card.suit = suit;
deck.add(card);
}
} 
return deck;
}
private static void swap(List<Card> deck, int i, int j) {
Card t = deck.get(i);
deck.set(i, deck.get(j));
deck.set(j, t);
}
private static void shuffle(List<Card> deck) {
Random random = new Random(20190905);
for (int i = deck.size() - 1; i > 0; i--) {
int r = random.nextInt(i);
swap(deck, i, r);
}
}
public static void main(String[] args) {
List<Card> deck = buyDeck();
System.out.println("刚买回来的牌:");
System.out.println(deck);
shuffle(deck);
System.out.println("洗过的牌:");
System.out.println(deck);
// 三个人,每个人轮流抓 5 张牌
List<List<Card>> hands = new ArrayList<>();
hands.add(new ArrayList<>());
hands.add(new ArrayList<>());
hands.add(new ArrayList<>());
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
hands.get(j).add(deck.remove(0));
}
} 
System.out.println("剩余的牌:");
System.out.println(deck);
System.out.println("A 手中的牌:");
System.out.println(hands.get(0));
System.out.println("B 手中的牌:");
System.out.println(hands.get(1));
System.out.println("C 手中的牌:");
System.out.println(hands.get(2));
}
}

Advantages and disadvantages of sequence table ArrayList storage structure

advantage

There is no need to add additional storage space to express the relationship between elements clearly.

Can quickly access elements at any position in the table

shortcoming

The time complexity of deleting and inserting elements at any position is O(N), so a large number of elements need to be moved.

When the length of the sequence table changes greatly, it is difficult to determine the capacity of the storage space.

It is easy to cause fragmentation of storage space, which is a waste of space.

Guess you like

Origin blog.csdn.net/paperjie/article/details/133025602