目录
顺序表与ArrayList
1. 手撕顺序表
2.ArrayList的使用
3.ArrayList的源码分析(扩容机制)
4.力扣题练习
顺序表与ArrayList
线性表是在逻辑上具备线性结构的一种有序序列,包括顺序表和链表。其中顺序表的物理地址也连续,一般采用数组储存,在数组上完成对数据的增删改查。链表的物理地址不连续,通过记录下一个节点的地址来实现逻辑上的连续,通过对记录地址变量的修改来实现增删改查。
1. 手撕顺序表
对于任意一个继承list接口的数据结构我们都应该实现增删改查获取长度清空等方法,以及相应类的构造方法,我们知道Java中为了提高代码的复用,都是通过类继承接口的方式来进行代码试现,下面让我们写这样一个接口。
下面我们简单通过数组来实现下这个顺序表:
注意:我们自己的顺序表没有实现泛型机制,而java提供的ArrayList是实现了泛型的。
import java.util.Arrays;
public class MyArrayList implements MyList{
private int[] array;
private int size;
MyArrayList(){
array=new int[10];
size=0;
}
//指定容量的构造器
MyArrayList(int initcapacity){
array=new int[initcapacity];
size=0;
}
@Override
public void add(int data) {
if(size==array.length){
Arrays.copyOf(array,array.length*2);
}
array[size]=data;
size++;
}
@Override
public void add(int pos, int data) {
if (pos>size||pos<0){
throw new posException();
}
if(size==array.length){
Arrays.copyOf(array,array.length*2);
}
for(int i=size-1;i>=pos;i--){
array[i+1]=array[i];
}
array[pos]=data;
size++;
}
@Override
public boolean contains(int toFind) {
for(int i=0;i<size;i++){
if(array[i]==toFind){
return true;
}
}
return false;
}
@Override
public int indexOf(int toFind) {
for (int i=0;i<size;i++){
if(array[i]==toFind){
return i;
}
}
return -1;
}
@Override
public int get(int pos) {
if (pos>=size||pos<0){
throw new posException();
}
return array[pos];
}
@Override
public void set(int pos, int value) {
if (pos>=size||pos<0){
throw new posException();
}
array[pos]=value;
}
@Override
public void remove(int toRemove) {
int num=indexOf(toRemove);
if(size==0){
throw new emptyException();
}
for(int i=num;i<size-1;i++){
array[i]=array[i+1];
}
size--;
}
@Override
public int size() {
return size;
}
@Override
public void clear() {
for (int i=0;i<size;i++){
array[i]=0;
}
size=0;
}
}
大家可以自己动手写一写这个代码实现。
2.ArrayList的使用
ArrayList基本的增删改查操作与上述几乎一致,这里重点讲述ArrayList的遍历方法:
最常见的遍历方式有四种,分别是:
1.for循环+下标:
for(int i=0;i<list.length;i++){
System.out.print(list[i]+" ");
}
2.for-each遍历:
ArrayList<String> list = new ArrayList();
for (String str:list) {
System.out.print(str+" ");
}
3.迭代器遍历:
ArrayList<String> list = new ArrayList();
Iterator iterator=list.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+" ");
}
4.集合对象的for-each方法(结合lambda表达式)
ArrayList<String> list = new ArrayList();
list.add("awda");
list.add("asdasd");
list.forEach(str->{
System.out.print(str+" ");
});
3.ArrayList的源码分析(扩容机制)
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);
}
return 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;
}
总结:
- ArrayList顺序表在无参构造时初始容量大小是10。
- 在添加元素时,检查是否需要扩容,如果是调用grow方法扩容。
-
初步预估按照 1.5 倍大小扩容如果用户所需大小超过预估 1.5 倍大小,则按照用户所需大小扩容真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
- 用Arrays.copyOf()方法进行扩容。
4.力扣题练习
问题链接:杨辉三角
问题:
思路: 通过泛型内在放一个顺序表类的方法实现二维列表,然后再利用循环先将每一层的数值填入numRows个顺序表中,再把这numRows个顺序表填入另一个顺序表中,最后这个顺序表就包含了我们想要的结果。
解题答案: