ArrayList与顺序表
1.线性表
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。
2.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。
public class MyArrayList {
public int[] elem;
public int usedSize;//存储了多少个有效的数据
public static final int DEFAULT_SIZE = 5;
public MyArrayList() {
this.elem = new int[DEFAULT_SIZE];
}
// 打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i] +" ");
}
System.out.println();
}
// 获取顺序表长度
public int size() {
return this.usedSize;
}
// 判定是否包含某个元素
public boolean contains(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return true;
}
}
return false;
}
// 查找某个元素对应的位置
public int indexOf(int toFind) {
for (int i = 0; i < this.usedSize; i++) {
if(this.elem[i] == toFind) {
return i;
}
}
return -1;//因为数组 没负数下标
}
// 新增元素,默认在数组最后新增
public void add(int data) {
if(this.isFull()) {
resize();
}
this.elem[this.usedSize] = data;
this.usedSize++;
}
/**
* 扩容
*/
private void resize() {
this.elem = Arrays.copyOf(this.elem,
2*this.elem.length);
}
/**
* 判断是否为满
* @return
*/
public boolean isFull() {
/*if(this.usedSize == this.elem.length) {
return true;
}
return false;*/
return this.usedSize == this.elem.length;
}
// 在 pos 位置新增元素 O(N)
public void add(int pos, int data) {
checkIndex(pos);
if(isFull()) {
resize();
}
for (int i = usedSize-1; i >= pos ; i--) {
elem[i+1] = elem[i];
}
elem[pos] = data;
usedSize++;
}
/**
* 检查add数据的时候,pos是否是合法的
* @param pos
*/
private void checkIndex(int pos) {
if(pos < 0 || pos > usedSize) {
throw new IndexOutOfException
("位置不合法,请检查位置的合法性!");
}
}
// 获取 pos 位置的元素
public int get(int pos) {
checkGetIndex(pos);
return elem[pos];
}
private void checkGetIndex(int pos) {
if(pos < 0 || pos >= usedSize) {
throw new IndexOutOfException
("get获取元素的时候,位置不合法,请检查位置的合法性!");
}
}
// 给 pos 位置的元素设为 value 1
public void set(int pos, int value) {
checkIndex(pos);
elem[pos] = value;
}
//删除第一次出现的关键字key O(n)
public boolean remove(int toRemove) {
int index = indexOf(toRemove);
if(index == -1) {
System.out.println("没有这个数据");
return false;
}
for (int i = index;i < usedSize-1;i++) {
elem[i] = elem[i+1];
}
usedSize --;
//elem[usedSize] = null;
// 如果里面是引用类型 那么此时就需要手动置空
elem[usedSize] = 0;
return true;
}
// 清空顺序表
public void clear() {
usedSize = 0;
}
}
3.ArrayList
【说明】
- ArrayList实现了RandomAccess接口,表明ArrayList支持 随机访问
- ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
- ArrayList实现了Serializable接口,表明ArrayList是持序列化的
- 和Vector不同,ArrayList不是线程安全的,在单线程下可以使用, 在多线程中可以选择Vector或者CopyOnWriteArrayList
- ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
构造方法:
方法 | 解释 |
---|---|
ArrayList( ) | 无参构造 |
ArrayList(Collection<? extends E> c) | 利用其他 Collection 构建 ArrayList |
ArrayList(int initialCapacity) | 指定顺序表初始容量 |
构造函数源码分析:
1.无参构造
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
总结:
- 最开始不会分配内存,初始容量为0
- 第一次Add()的时候分配10个内存
- 按照1.5倍扩容,例如插入第11个元素的时候,数组容量是15,而不是11
- ArrayList自动扩容
2.指定数组长度构造
ArrayList<Integer> list = new ArrayList<>(15);//指定数组长度
3.用一个list作为参数构造
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(11);
list1.add(12);
list1.add(13);
ArrayList<Integer> list2 = new ArrayList<>(list1);
list2.add(1);
System.out.println(list2);
常用方法:
方法 | 解释 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E element) | 将 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 删除 index 位置元素 |
boolean remove(Object o) | 删除遇到的第一个 o |
E get(int index) | 获取下标 index 位置元素 |
E set(int index, E element) | 将下标 index 位置元素设置为 element |
void clear() | 清空 |
boolean contains(Object o) | 判断 o 是否在线性表中 |
int indexOf(Object o) | 返回第一个 o 所在下标 |
int lastIndexOf(Object o) | 返回最后一个 o 的下标 |
List subList(int fromIndex, int toIndex) | 截取部分 list |
- 在使用remove()需要注意,源码中有指定下标删除和指定元素删除,如果我们ArrayList设置的是Integer,删除元素remove(new Integer())
- 指定位置插入元素,不是一个元素一个元素的往后移动,而是以数组拷贝的形式,把所有要移动的元素当做一个整体进行移动
ArrayList遍历:
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) + " ");
}
- 方法二:foreach循环
for (Integer integer : list) {
System.out.print(integer + " ");
}
- 方法三:使用迭代器
//listIterator只对list有效,可以双向迭代
//Iterator<Integer> it = list.listIterator();
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
System.out.print(it.next() + " ");
}
4.oj题
1.给定字符串s1、s2,要求删除s1中s2出现的所有元素
示例:
s1=“welcome to bit”
s2=“come”
输出:“wl t bit”
思路:遍历第s1中的每个字符,如果这个字符不包含在s2,那么就添加到list,最后遍历list
public static void main(String[] args) {
String s1 = "welcome to bit";
String s2 = "come";
List<Character> list = new ArrayList<>();
for (int i = 0; i < s1.length(); i++) {
char ch = s1.charAt(i);
//contains()要求提供CharSequence类型,所以有两个方法,第一个是加个"",第二个是使用String.valveOf(ch)
if (!s2.contains(ch+"")) {
list.add(s1.charAt(i));
}
}
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i));
}
}
2.杨辉三角 力扣链接
给定一个非负整数 *numRows
,*生成「杨辉三角」的前 numRows
行。
示例 :
输入: numRows = 5
输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]
思路:通过二维数组思维进行考虑
class Solution {
public List<List<Integer>> generate(int numRows) {
List<List<Integer>> list = new ArrayList<>();
List<Integer> row = new ArrayList<>();
//第一行是固定的
row.add(1);
list.add(row);
//由于第一行是固定的只有一个1,所以i从1开始
for(int i=1;i<numRows;i++){
//每一行就是一个list
List<Integer> curRow = new ArrayList<>();
//第一个元素为1
curRow.add(1);
//中间数据
List<Integer> prevRow = list.get(i-1);//找到上一行
//j不能等于i,因为i表示最后一个元素,最后一个元素固定是1
for(int j = 1 ; j < i ; j++){
int x = prevRow.get(j-1)+prevRow.get(j);
curRow.add(x);
}
//最后一个元素也为1
curRow.add(1);
list.add(curRow);
}
return list;
}
}