红魔咖啡馆

头发越掉越多,头发越掉越少

0%

【CS61B】Lec6-ArrayLists

ArrayList

Array中的随机访问

根据指定位置获取a数组中的元素是很快的,原因如下:

  • 与数组大小无关
  • 内存空间其实都是相同的大小

实现

添加删除元素

观察发现:

  • 元素个数可以用size表示
  • 在结尾添加元素时,添加的位置其实是第size位
  • 而获得结尾元素时,结尾元素位于size-1的位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void addLast(int x){
        items[size] = x;
}

public int getLast(){
    return items[size-1];
}

public int get(int i){
    return items[i];
}

public int removeLast(){
    int itemToReturn = getLast();
    size -=1;
    return itemToReturn;
}

扩容

当列表满了以后,还想扩容并且增加容量,需要创建一个新数组,并调整size,将新元素加入数组

1
2
3
4
5
6
7
8
9
10
11
12
13
private void resize(int capacity){
        int[] a = new int[capacity];
        System.arraycopy(items, 0, a, 0, size);
        items = a;
}

public void addLast(int x){
    if (items.length == size){
        resize(size+1);
    }
    items[size] = x;
    size++;
}

这里将resize作为一个private方法,即调用addLast时,若发现空间不够,则会自动调用resize扩容,而不用手动调用

但每次扩容都会复制一份数组,扩容次数多了,创建的数组空间就会变大,导致性能下降

我们可以在每次扩容时在原大小基础上乘一个常量以一次性扩大大量空间,可以有效解决性能问题(Python的列表就是这样的原理)

但有时候不需要那么多空间,会造成空间浪费,故在发现利用率不够时,我们需要缩小数组尺寸:

  • 定义一个使用比率:\(R = \frac{\text{size}}{\text{items.length}}\)
  • 常用方案位当\(R<0.25\)时,将size减半

泛型化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package lec7;

public class ArrayList<T> {
    private T[] items;
    private int size;
    public ArrayList(){
        size = 0;
        items = (T[]) new Object[100];
    }

    private void resize(int capacity){
            T[] a = (T[])new Object[capacity];
            System.arraycopy(items, 0, a, 0, size);
            items = a;
    }

    public void addLast(T x){
        if (items.length == size){
            resize(size+1);
        }
        items[size] = x;
        size++;
    }

    public T getLast(){
        return items[size-1];
    }

    public T get(int i){
        return items[i];
    }

    public T removeLast(){
        T itemToReturn = getLast();
        items[size-1] = null;
        size -=1;
        return itemToReturn;
    }
}

有一个问题:在创建泛型T的数组引用时,会用到(T[]) new Object[100]这样的创建方法,即对类型进行了强制类型转换,IDEA会报warning:未检查的转换,此时可以忽略

在泛型的情况下,对于删除元素最好在减小size的同时将数组对应位置设置为null,这样垃圾回收器可以发现并回收内存,因为泛型导致了存入数组的数据类型大小可能会很大,从而浪费空间