目录
1.List接口
1.1 List接口的集合关系网络
1.2 List的使用
2. ArrayList与顺序表
2.1 线性表
2.2 顺序表
2.3 ArrayList
2.3.1 ArrayList的集合关系网络
2.3.2 ArrayList的使用
2.3.2.1 ArrayList的构造方法
2.3.2.2 ArrayList的扩容机制逻辑示图如下:
2.3.2.3 常用方法
2.3.2.4 ArrayList的遍历
2.3.3 ArrayList的具体使用
2.3.3.1 字符串处理问题
2.3.3.2 杨辉三角
2.3.3.3 简单的洗牌算法
2.3.4 ArrayList特点
在集合中,List是一个接口,继承自Collection,Collection也是一个接口;
在数据结构中,List是一个线性表,即n个具有想同类型元素的有限序列,在该序列上可以执行增删查改等操作;
List是一个接口,并不能直接用来实例化,如果要使用就必须实例化List的实现类,在集合框架中,ArrayList和LinkedList都实现了List接口;
接下来将依次展示List的实现类使用方法,本篇为ArrayList;
线性表是n个具有相同特性的数据元素的有限序列,线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列等等;
线性表在逻辑上是线性结构,即连续的一条直线,但在物理结构上不一定连续。
线性表在物理存储时通常以数组和链式的形式存储;
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储,在数组上完成数据的增删查改;
下面展示顺序表常用接口的模拟实现:
(1)包类示图:
(2)MyArrayList:
package TestMyArrayList;
import java.util.Arrays;
public class MyArrayList {
public int[] elem;
public int useSize; // 记录顺序表存储的有效数据个数
public static final int DEFAULT_SIZE=10;
public MyArrayList(){
this.elem = new int[DEFAULT_SIZE];
}
// 成员方法1:打印顺序表
public void display(){
for(int i=0;i=pos;i--){
elem[i+1]=elem[i];
}
this.elem[pos]=data;
this.useSize++;
}
// 成员方法9:判插入数据时,pos是否合法
private void checkAddIndex(int pos){
if(pos<0||pos>this.useSize){
throw new AddIndexOutOfException("pos is illegal!");
}
}
// 成员方法10:获取pos位置的元素
public int get(int pos){
checkAddIndex(pos);
return this.elem[pos];
}
// 成员方法11:判获取数据时,pos是否合法
private void checkGetIndex(int pos){
if(pos<0||pos>=this.useSize){
throw new AddIndexOutOfException("pos is illegal!");
}
}
// 成员方法12:将pos位置的数据设置为value
public void set(int pos, int value){
checkGetIndex(pos);
this.elem[pos]=value;
}
// 成员方法13:删除第一次出现的关键字key
public boolean remove(int toRemove){
int index=indexOf(toRemove);
if(index==-1){
System.out.println("data is non existent!");
return false;
}
for(int i=index;i
(3)GetIndexOutOfException
package TestMyArrayList;
public class GetIndexOutOfException extends RuntimeException{
public GetIndexOutOfException(){
}
public GetIndexOutOfException(String message){
super(message);
}
}
(4)AddIndexOutOfException
package TestMyArrayList;
public class AddIndexOutOfException extends RuntimeException{
public AddIndexOutOfException(){
}
public AddIndexOutOfException(String message){
super(message);
}
}
说明:
(1)ArrayList是以泛型方式实现的,使用时必须实例化;
(2)ArrayList实现了RandomAccess接口,表明ArrayList支持随机访问;
(3)ArrayList实现了Clone接口,表明ArrayList是可以clone的;
(4)ArrayList实现了Serializable接口,表明ArrayList是支持序列化的;
(对象->字符串:序列化;字符串->对象:反序列化)
(5)相比于Vector,ArrayList不是线程安全的,在单线程下可使用,在多线程中可以选择Vector或者CopyOnWriteArrayList;
(6)ArrayList底层是一段连续空间,并且可以动态扩容,是一个动态类型的顺序表;
ArrayList的构造方法有以下三种:
示例代码如下:
(1)构造方法1:(无参构造器)
// 构造方法1:
ArrayList arrayList1 = new ArrayList<>();
arrayList1.add(97);
arrayList1.add(8);
arrayList1.add(5);
System.out.println(arrayList1);
输出结果为:
(2)构造方法2:(带有初始容量的构造方法)
// 构造方法2:
ArrayList arrayList2 = new ArrayList<>(20);
arrayList2.add(1997);
arrayList2.add(85);
arrayList2.add(25);
System.out.println(arrayList2);
输出结果为:
(3)构造方法3:
// 构造方法3:
LinkedList list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);
ArrayList arrayList3 = new ArrayList<>(list);
ArrayList arrayList4 = new ArrayList<>(list);
System.out.println(arrayList3);
System.out.println(arrayList4);
输出结果为:
第三种构造方法的含义为:将另外一个实现了Collection接口的具体类作为构造ArrayList的具体参数,
且满足:该具体类的泛型参数是ArrayList实例化对象的泛型参数本身或其子类,
即可将该类中的所有元素添加至ArrayList实例化的该对象中来;
简而言之:
在实例化ArrayList对象进行new操作时,其实并没有为该对象分配内存,但在第一次进行add时,会调用grow方法为其分配长度为10的内存大小;
且扩容方式为1.5倍扩容法;
方法 | 解释 |
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 |
截取部分list |
方法示例:
(1)E remove(int index) 与 boolean remove(Object o):
ArrayList arrayList = new ArrayList<>();
arrayList.add(2);
arrayList.add(3);
System.out.println(arrayList);
arrayList.add(0,1);
System.out.println(arrayList);
arrayList.remove(2);
System.out.println(arrayList);
arrayList.remove(new Integer(1));
System.out.println(arrayList);
输出结果为:
(2)List
ArrayList arrayList1 = new ArrayList<>();
for(int i=0;i<10;i++){
arrayList1.add(i*2+1);
}
System.out.println(arrayList1);
List arrayList2 = arrayList1.subList(3,6);
System.out.println(arrayList2);
输出结果为:
注:原ArrayList对象arrayList1和截取的子串arrayList2共用一个elemData数组,修改元素时二者相互影响:
ArrayList arrayList1 = new ArrayList<>();
for(int i=0;i<10;i++){
arrayList1.add(i*2+1);
}
System.out.println("Before modify: ");
System.out.println("arrayList1: "+arrayList1);
List arrayList2 = arrayList1.subList(3,6);
System.out.println("arrayList2: "+arrayList2);
arrayList2.set(0,999);
System.out.println("After modify: ");
System.out.println("arrayList1: "+arrayList1);
System.out.println("arrayList2: "+arrayList2);
输出结果为:
方法1:直接输出:
ArrayList类并没有重写toString方法,其父类AbstractList也没有重写toString方法,但是List接口有toString方法,故而可以直接使用System.out.println()方法输出ArrayList对象;
上文代码已有示例,此处不再赘述;
方法2:for循环:
ArrayList arrayList = new ArrayList<>();
arrayList.add(1997);
arrayList.add(8);
arrayList.add(5);
arrayList.add(25);
for(int i=0;i
输出结果为:
方法:3:for-each:
ArrayList arrayList = new ArrayList<>();
arrayList.add(1997);
arrayList.add(8);
arrayList.add(5);
arrayList.add(25);
for(Integer x:arrayList){
System.out.print(x+" ");
}
输出结果为:
方法4:迭代器:
迭代器方法源码如下:
试运行以下代码:
ArrayList arrayList = new ArrayList<>();
arrayList.add(1997);
arrayList.add(8);
arrayList.add(5);
arrayList.add(25);
ListIterator it = arrayList.listIterator();
while(it.hasNext()){
System.out.print(it.next()+" ");
// 打印it的同时令it前行一步
}
输出结果为:
题目描述:现有字符串s1:"Welcome to java"和字符串s2:"come",试将s1中包含的s2的字符删去后的结果;
思路:遍历字符串s1,当s2不包含s1当前的字符则添加至一个ArrayList对象中,输出该对象结果即可;
代码如下:
public static void main(String[] args) {
List list = new ArrayList<>();
String s1 = "Welcome to java";
String s2 = "come";
for(int i=0;i
输出结果为:
题目描述:
题目链接如下:118. 杨辉三角 - 力扣(LeetCode)
代码如下:
class Solution {
public List> generate(int numRows) {
//List嵌套List即二维数组
List> ret = new ArrayList<>();
List row = new ArrayList<>();
row.add(1); // 初始化第一行(只有一个元素,为1)
ret.add(row); // 创建二维数组
for(int i=1;i previousRow = ret.get(i-1); //获取上一行元素
List currentRow = new ArrayList<>();
currentRow.add(1); // 每一行第一个元素为1
// 每一行非两端位置元素赋值
for(int j=1;j
通过用例测试,参考输出示例如下:
(1)创建包类关系如下:
(2)Poker类:
package TestDemo1;
public class Poker {
private String suit; // 花色
private int rank; //数字
public Poker(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public String getSuit() {
return suit;
}
public void setSuit(String suit) {
this.suit = suit;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
@Override
public String toString() {
return "{" + suit+" "+ rank+ "}";
}
}
(3) Game类:
package TestDemo1;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
public class Game {
private static final String[] suits = {"♥","♣","♦","♠"};
public List buyPoker(){
List pokers = new ArrayList<>();
for(int i=0;i<4;i++){
for(int j=1;j<=13;j++){
String suit = suits[i]; // 花色
int rank = j; // 数字
Poker poker = new Poker(suit,j);
pokers.add(poker);
}
}
return pokers;
}
// 洗牌
public void shuffle(List pokers){
for(int i=pokers.size()-1; i>0;i--){
Random random = new Random();
int index = random.nextInt(i);
Swap(pokers,i,index);
}
}
private void Swap(List pokers,int i,int j){
Poker tmp = pokers.get(i);
pokers.set(i,pokers.get(j));
pokers.set(j,tmp);
}
// 揭牌:三个人轮流揭5张牌
// 每次删除pokers的0下标的元素,并依次添加至3个ArrayList对象中
public List> game(List pokers){
// 创建玩家List
List> players = new ArrayList<>();
List player1 = new ArrayList<>();
List player2 = new ArrayList<>();
List player3 = new ArrayList<>();
players.add(player1);
players.add(player2);
players.add(player3);
for(int i=0;i<5;i++){ // 控制揭牌揭5轮
for(int j=0;j<3;j++){ // 控制每个玩家每次揭牌揭一张
Poker currentPoker = pokers.remove(0);
// ArrayList的remove方法会将查找下标对应的元素返回
players.get(j).add(currentPoker);
}
}
return players;
}
}
(4)Test类:
package TestDemo1;
import java.util.List;
public class Test {
public static void main(String[] args) {
Game game = new Game();
List pokers = game.buyPoker();
int count=1;
for(Poker x:pokers){
System.out.print(x+" ");
count++;
if(count%10==0){
System.out.println();
}
}
game.shuffle(pokers);
System.out.println();
System.out.println("------------------------------------------------------------------");
System.out.println("洗牌:");
count=1;
for(Poker x:pokers){
System.out.print(x+" ");
count++;
if(count%10==0){
System.out.println();
}
}
System.out.println();
System.out.println("------------------------------------------------------------------");
System.out.println("揭牌:");
List> players = game.game(pokers);
for(int i=0;i
(5)输出结果为:
注:ArrayList类的remove方法源码:
可见其逻辑为:删除哪个下标的值,就将该下标的元素作为返回值进行返回;
优点:(1)当给定下标时,ArrayList的查找速度非常快。故而ArrayList适合给定下标的查找,复杂度为O(1);
缺点:(1)任意位置插入或删除元素时,需要大量挪动元素,故而ArrayList不适合应用于任意位置插入和删除较为频繁的场景;
(2)每次扩容时也面临着资源浪费的问题,同时申请新空间,拷贝数据再释放旧空间也有不小的消耗;