一.线性表
线性表是n个具有相同特性的元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表包括:顺序表,链表,栈,队列。
线性表在逻辑上是线性结构,也就是连续的一条直线。但是在物理结构上不一定是连续的,线性表在物理上存储时,常以数组和链式结构的形式存储。
二.顺序表
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删改查。
三.ArrayList简介
1. ArrayList是以泛型方式实现的,使用时必须要先实例化
2. ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问
3.ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
4.ArrayList实现了Cloneable接口,表明ArrayList是可以clone的
5.和Vector不同,ArrayList不是线程安全的,在单线程下可以使用,在多线程中可以选择Vector或者CopyOnWriteArrayList
6. ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表
四.ArrayList的使用
1.构造
方法 | 解释 |
ArrayList() | ArrayList(int initialCapacity) |
ArrayList(Collection<? extends E> c) | 利用其他 Collection 构建 ArrayList |
ArrayList(int initialCapacity) | 利用其他 Collection 构建 ArrayList |
代码示例:
public static void main(String[] args) {
//创建空列表
ArrayList<Integer> list1 = new ArrayList<>();
//创建容量为5的列表
ArrayList<Integer> list2 = new ArrayList<>(5);
list2.add(1);
list2.add(2);
for (Integer i : list2) {
System.out.print(i+" ");
}
System.out.println();
//创建与内容list2相同的列表
ArrayList<Integer> list3 = new ArrayList<>(list2);
for (Integer i : list3) {
System.out.print(i+" ");
}
System.out.println();
list3.add(3);
for (Integer i : list3) {
System.out.print(i+" ");
}
}
运行结果:
注意:如果省略类型,JVM不会报错,但如果一个列表可以存放任意类型的元素,会在使用中造成不良影响
2.常见操作
方法 | 解释 | ||
boolean add(E e) | 尾插 e | ||
boolean add(E e) | 将 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<E> subList(int fromIndex, int toIndex) | 返回最后一个 o 的下标 |
3.ArrayList的遍历
1.for循环+下标
2.foreach
3.foreach
代码示例:
public static void main(String[] args) {
List<Integer> test = new ArrayList<>();
test.add(1);
test.add(2);
test.add(3);
test.add(4);
test.add(5);
//for循环+下标
int size = test.size();
for (int i = 0; i < size; i++) {
System.out.print(test.get(i)+" ");
}
System.out.println();
//foreach
for (Integer i : test) {
System.out.print(i+" ");
}
System.out.println();
//迭代器
System.out.println("使用迭代器输出1");
Iterator<Integer> it = test.iterator();
while (it.hasNext()) {
System.out.print(it.next()+" ");
}
System.out.println();
System.out.println("使用迭代器输出2");
ListIterator<Integer> it2 = test.listIterator();
//ListIterator实现了Iterator接口,拓展了Iterator的功能
while (it2.hasNext()) {
System.out.print(it2.next()+" ");
}
System.out.println();
System.out.println("迭代器,但是从后往前");
ListIterator<Integer> it3 = test.listIterator(test.size());
//ListIterator实现了Iterator接口,拓展了Iterator的功能
while (it3.hasPrevious()) {
System.out.print(it3.previous()+" ");
}
}
运行结果:
4.ArrayList的扩容机制
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;
}
从源码中可以看出:
1.检测是否需要扩容,是则调用grow准备扩容
2.估算所需容量大小,初步预估按照1.5倍大小扩容,超过则按需求大小扩容。真正扩容之前检测是否能扩容成功,防止太大导致扩容失败
3.使用copyOf进行扩容
五.使用ArrayList的经典例题
1.删除一个字符串中所有另一字符串的字符
代码示例:
public static void main(String[] args) {
String str1 = "welcome to java";
String str2 = "come";
ArrayList<Character> ret = new ArrayList<>();
for (int i = 0; i < str1.length(); i++) {
char c = str1.charAt(i);
if(!str2.contains(c + "")) {
//此处变量要求是字符串类型,通过c + ""将char转化为String
ret.add(c);
}
}
for (int i = 0; i < ret.size(); i++) {
System.out.print(ret.get(i));
}
System.out.println();
}
运行结果:
2.杨辉三角
代码示例:
import java.util.ArrayList;
import java.util.List;
public class Test1 {
public static List<List<Integer>> generate(int numRows) {
List<List<Integer>> ret = new ArrayList<>();
List<Integer> list0 = new ArrayList<>();
//初始化第一行
list0.add(1);
ret.add(list0);
for (int i = 1; i < numRows; i++) {
//处理第一个元素
List<Integer> curRow = new ArrayList<>();
curRow.add(1);
//中间
List<Integer> preRow = ret.get(i - 1);
for (int j = 1; j < i; j++) {
int val1 = preRow.get(j); //上一行的同一列
int val2 = preRow.get(j-1); //上一行的前一列
curRow.add(val1+val2);
}
//尾
curRow.add(1);
ret.add(curRow);
}
return ret;
}
public static void main(String[] args) {
List<List<Integer>> triangle = generate(5);
for (List<Integer> row : triangle) {
System.out.println(row);
}
}
}
运行结果:
3.洗牌问题
代码示例:
Card.java
package card;
public class Card {
public String suit;
public int rank; //值
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String toString() {
return String.format("{%s %d}", suit, rank);
}
}
CardDemo.java
package card;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class CardDemo {
public static final String[] suits = {"♠", "♥", "♣", "♦"};
//买一副牌
public List<Card> buyCard() {
List<Card> cardList = new ArrayList<>(52);
for (int i = 1; i <= 13; i++) {
for (int j = 0; j < 4; j++) {
int rank = i;
String suit = suits[j];
Card card = new Card(suit, rank);
cardList.add(card);
}
}
return cardList;
}
public void shuffle(List<Card> cardList) {
Random random = new Random();
for (int i = cardList.size() - 1; i > 0; i--) {
int index = random.nextInt(i);
swap(cardList, i, index);
}
}
private void swap(List<Card> cardList, int i, int j) {
Card temp = cardList.get(i);
cardList.set(i, cardList.get(j));
cardList.set(j, temp);
}
//3个人每个人 轮流揭牌5张
public List<List<Card>> play(List<Card> cardList) {
//三个人
List<Card> hand0 = new ArrayList<>();
List<Card> hand1 = new ArrayList<>();
List<Card> hand2 = new ArrayList<>();
//创建一个新的二维数组,将前面创建的添加进去
List<List<Card>> hand = new ArrayList<>();
hand.add(hand0);
hand.add(hand1);
hand.add(hand2);
//三个人轮流揭牌五张
for (int i = 0; i < 5; i++) { //每个人的第一张牌
for (int j = 0; j < 3; j++) { //每个人
Card card = cardList.remove(0);
//添加抽中的牌到对应的人
hand.get(j).add(card);
}
}
return hand;
}
}
Test.java
package card;
import java.util.*;
public class Test {
public static void main(String[] args) {
CardDemo cardDemo = new CardDemo();
//1. 买52张牌
List<Card> cardList = cardDemo.buyCard();
System.out.println(cardList);
//2. 洗牌
cardDemo.shuffle(cardList);
System.out.println(cardList);
//3. 3个人 每个人 轮流揭牌5张
List<List<Card>> ret = cardDemo.play(cardList);
for (int i = 0; i < ret.size(); i++) {
System.out.println("第" + (i + 1) + "个人的牌:" + ret.get(i));
}
System.out.println("剩下的牌:");
System.out.println(cardList);
}
}
运行结果: