目录
一、List
1.定义
List接口是Java集合框架中的一种接口,它代表了一个有序的、可重复的元素序列。List接口继承自Collection接口,定义了一些用于操作列表的方法。
常见的List实现类有ArrayList和LinkedList。ArrayList是基于动态数组实现的,它支持快速随机访问和随机插入/删除操作,但插入/删除操作涉及数组元素的移动,性能相对较低。LinkedList则是基于双向链表实现的,它支持快速插入/删除操作,但访问元素需要从头部或尾部开始遍历链表,性能相对较低。
2.常用方法
方法
|
解释
|
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<E> subList(int fromIndex, int toIndex)
| 截取部分 list |
二、Arraylist
1.定义
ArrayList是List接口的一个实现类,它是基于动态数组的数据结构。ArrayList可以存储任意类型的元素,并且允许元素的重复。
ArrayList的优点是支持快速随机访问,可以通过索引来直接访问和修改元素,时间复杂度为O(1)。同时,ArrayList实现了可变长度的数组,可以自动扩容和缩容,具有较高的灵活性。
2.自己实现Arraylist的部分方法
实现List接口:
package listtest;
public interface Ilist {
//新增元素,默认在数组最后新增
public void add(int data);
boolean isFull();
// 在 pos 位置新增元素
public void add(int pos, int data);
//判定是否包含某个元素
public boolean contains(int toFind);
//查找某个元素对应的位置
public int indexOf(int toFind);
//获取 pos 位置的元素
public int get(int pos);
//给 pos 位置的元素设为 value
public void set(int pos, int value);
//删除第一次出现的关键字key
public void remove(int toRemove);
//获取顺序表长度
public int size() ;
//清空顺序表
public void clear();
//打印顺序表,注意:该方法并不是顺序表中的方法,为了方便看测试结果给出的
public void display() ;
}
MyArrayList:
//MyArrayList.java
package listtest;
import java.util.Arrays;
public class MyArrayList implements Ilist{
public int[] array;
public int usedSize;
public static final int DEFAULT_CAPACITY = 10;
public MyArrayList() {
this.array = new int[DEFAULT_CAPACITY];
}
@Override
public void add(int data) {
if(isFull()){
grow(array);
}
this.array[usedSize] = data;
this.usedSize++;
}
private void grow(int[] array){
this.array = Arrays.copyOf(this.array,this.array.length*2);
}
@Override
public boolean isFull() {
return this.usedSize == this.array.length;
}
@Override
public void add(int pos, int data) {
try{
checkPos(pos);
if(isFull()){
grow(array);
}
for (int i = usedSize - 1; i < pos ; i++) {
array[i+1] = array[i];
}
array[pos] = data;
this.usedSize++;
}catch(PosIllegal e){
System.out.println("pos位置不合法!!");
e.printStackTrace();
}
}
public void checkPos(int pos) throws PosIllegal{
if(pos < 0 || pos > usedSize){
throw new PosIllegal("pos不合法!!");
}
}
public void checkPos1(int pos) throws PosIllegal{
if(pos < 0 || pos > usedSize){
throw new PosIllegal("pos不合法!!");
}
}
@Override
public boolean contains(int toFind) {
for (int i : array) {
if (array[i] == toFind) {
return true;
}
}
return false;
}
@Override
public int indexOf(int toFind) {
for (int i = 0; i < array.length ; i++) {
if (array[i] == toFind){
return i;
}
}
return 0;
}
@Override
public int get(int pos) {
try{
checkPos1(pos);
checkEmpty();
return array[pos];
}catch(PosIllegal e){
System.out.println("pos位置不合法!!");
e.printStackTrace();
}catch(EmptyException e){
System.out.println("顺序表为空!!");
e.printStackTrace();
}
return -1;
}
public void checkEmpty() throws EmptyException{
if(this.usedSize == 0){
throw new EmptyException("顺序表为空!!");
}
}
@Override
public void set(int pos, int value) {
this.array[pos] = value;
}
@Override
public void remove(int toRemove) {
try {
checkEmpty();
int pos = indexOf(toRemove);
if(pos == -1) {
return;
}
for (int i = pos; i < usedSize-1; i++) {
array[i] = array[i+1];
}
array[usedSize-1] = 0;
usedSize--;
}catch (EmptyException e) {
e.printStackTrace();
}
}
@Override
public int size() {
return this.usedSize;
}
@Override
public void clear() {
for (int i = 0; i < usedSize; i++) {
array[i] = 0;
}
usedSize = 0;
}
@Override
public void display() {
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
System.out.println();
}
}
//PosIllegal.java
package listtest;
public class PosIllegal extends RuntimeException{
public PosIllegal(){
}
public PosIllegal(String msg){
super(msg);
}
}
//EmptyExecption.java
package listtest;
public class EmptyException extends RuntimeException{
public EmptyException(){
}
public EmptyException(String msg){
super(msg);
}
}
test:
package listtest;
public class Test {
public static void main(String[] args) {
MyArrayList myArrayList = new MyArrayList();
myArrayList.add(1);
myArrayList.add(2);
myArrayList.add(3);
myArrayList.add(4);
myArrayList.add(5);
myArrayList.display();
System.out.println(myArrayList.isFull());
myArrayList.add(5,6);
myArrayList.display();
System.out.println(myArrayList.contains(4));
System.out.println(myArrayList.indexOf(4));
System.out.println(myArrayList.get(4));
myArrayList.set(0,99);
myArrayList.display();
myArrayList.remove(5);
myArrayList.display();
System.out.println(myArrayList.size());
myArrayList.clear();
myArrayList.display();
}
}
运行结果:
1 2 3 4 5 0 0 0 0 0
false
1 2 3 4 5 6 0 0 0 0
true
3
5
99 2 3 4 5 6 0 0 0 0
99 2 3 4 6 0 0 0 0 0
5
0 0 0 0 0 0 0 0 0 0
3.Arraylist使用
要使用ArrayList,首先需要导入java.util包。然后可以通过以下方式创建一个ArrayList对象:
import java.util.ArrayList;
// 创建一个ArrayList对象
//方法一
ArrayList<String> list = new ArrayList<String>();
//方法二
List<Integer> list1 = new ArrayList<Integer>();
方法一通过List这个引用可以调用当前类的所有可被调用的方法;
方法二只要实现这个接口的都能引用(向上转型),通过这个接口只能调用这个接口当中包含的方法。
接下来可以使用ArrayList提供的方法进行元素的操作,例如:
// 添加元素
list.add("A");
list.add("B");
list.add("C");
// 获取元素
String element = list.get(0); // 获取第一个元素
// 修改元素
list.set(1, "D"); // 将第二个元素修改为"D"
// 删除元素
list.remove(2); // 删除第三个元素
// 计算元素个数
int size = list.size(); // 获取列表中元素的个数
// 遍历元素
for (String item : list) {
System.out.println(item);
}
需要注意的是,ArrayList是非线程安全的,不适用于多线程环境。如果需要在多线程环境中使用,可以考虑使用线程安全的替代类Vector或使用Collections工具类的synchronizedList方法来生成同步的ArrayList对象。
此外,ArrayList支持泛型,可以指定ArrayList中元素的类型。在上面的例子中,我们指定ArrayList中存储的元素为String类型。
4.ArrayList常见操作
方法
|
解释
|
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<E> subList(int fromIndex, int toIndex) |
截取部分 list
|
常用操作:
添加元素:可以使用add()
方法向ArrayList中添加元素。例如:
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
获取元素:可以使用get()
方法来获取ArrayList中指定位置的元素。索引从0开始。例如:
String element = list.get(0);
修改元素:可以使用set()
方法来修改ArrayList中指定位置的元素。例如:
list.set(1, "C");
删除元素:可以使用remove()
方法来删除ArrayList中指定位置的元素。例如:
list.remove(0);
判断元素是否存在:可以使用contains()
方法来判断ArrayList中是否包含指定元素。例如:
boolean contains = list.contains("A");
获取元素索引:可以使用indexOf()
方法来获取元素在ArrayList中的索引。如果元素不存在,则返回-1。例如:
int index = list.indexOf("B");
获取ArrayList大小:可以使用size()
方法来获取ArrayList中元素的数量。例如:
int size = list.size();
清空ArrayList:可以使用clear()
方法将ArrayList中的所有元素清除。例如:
list.clear();
注意:
- add方法中,如果空间不够用 ,系统自动1.5倍扩容
- addAll方法中,“?”是通配符
- remove方法中,下面两种要分清楚:
list.remove(1);//删除下标为1的元素 list.remove(new Integer(1));//删除值为1的元素
subList方法并没有产生新的对象,而是直接返回截取部分的地址
5.ArrayList的遍历
public static void main(String[] args) {
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) + " ");
}
System.out.println();
// 借助foreach遍历
for (Integer integer : list) {
System.out.print(integer + " ");
}
System.out.println();
// 使用迭代器
Iterator<Integer> it = list.listIterator();
while(it.hasNext()){
System.out.print(it.next() + " ");
}
System.out.println();
}
运行结果:
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
6.ArrayList应用
6.1删除字符
str1:Hello world str2:eo
删除str1中出现的所有str2的字符,删除后结果为Hll wrld。
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
String str1 = "Hello world";
String str2 = "eo";
ArrayList<Character> ret = new ArrayList<>();
for (int i = 0; i < str1.length(); i++) {
char ch = str1.charAt(i);
if(!str2.contains(ch+"")){
//contains方法的参数是字符串,用“ ch+“” ”的形式将字符变为字符串
ret.add(ch);
}
}
for (int i = 0; i < ret.toArray().length; i++) {
System.out.print(ret.get(i) + " ");
}
}
}
运行结果:
H l l w r l d
6.2杨辉三角
给定一个非负整数numRows,生成杨辉三角的前numRows行。
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<List<Integer>> ret = generate(6);
for (int i = 0; i < ret.size(); i++) {
for (int j = 0; j < ret.get(i).size(); j++) {
System.out.print(ret.get(i).get(j)+" ");
}
System.out.println();
}
}
public static List<List<Integer>> generate(int numRows){
//List<List<Integer>>相当于二维数组
List<List<Integer>> ret = new ArrayList<>();
List<Integer> List0 = new ArrayList<>();
ret.add(List0);
List0.add(1);
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++) {
curRow.add(preRow.get(j-1) + preRow.get(j));
}
//每一行最后一个元素
curRow.add(1);
ret.add(curRow);
}
return ret;
}
}
运行结果:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1
6.3扑克牌
买牌——>洗牌——>摸牌
//Cards.java
package Card;
public class Cards {
public int num;
public String suit;
public Cards(int num, String suit) {
this.num = num;
this.suit = suit;
}
@Override
public String toString() {
return "[" + num + suit + "]";
}
}
//CardDemo.java
package Card;
import java.util.ArrayList;
import java.util.Random;
public class CardDemo {
public static final String[] suits = {"♥","♠","♣","♦"};
public ArrayList<Cards> buyCards(){
ArrayList<Cards> cardList = new ArrayList<>();
for (int i = 0; i < 13; i++) {
for (int j = 0; j < 4; j++) {
int num = i + 1;
String suit = suits[j];
Cards card = new Cards(num,suit);
cardList.add(card);
}
}
return cardList;
}
public void shuffle(ArrayList<Cards> cardList){
Random random = new Random();
for (int i = cardList.size() - 1; i > 0; i--) {
int j = random.nextInt(i);
swap(cardList, i, j);
}
}
private void swap(ArrayList<Cards> cardList ,int i , int j){
Cards tmp = cardList.get(i);
cardList.set(i,cardList.get(j));
cardList.set(j,tmp);
}
public ArrayList<ArrayList<Cards>> play(ArrayList<Cards> cardList){
ArrayList<Cards> player1 = new ArrayList<>();
ArrayList<Cards> player2 = new ArrayList<>();
ArrayList<Cards> player3 = new ArrayList<>();
ArrayList<ArrayList<Cards>> players = new ArrayList<>();
players.add(player1);
players.add(player2);
players.add(player3);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
players.get(j).add(cardList.remove(0));
}
}
return players;
}
}
//Test.java
package Card;
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
CardDemo cardDemo = new CardDemo();
System.out.print("买牌:");
ArrayList<Cards> cardList = cardDemo.buyCards();
System.out.println(cardList);
System.out.print("洗牌:");
cardDemo.shuffle(cardList);
System.out.println(cardList);
System.out.println("摸牌:");
ArrayList<ArrayList<Cards>> ret = cardDemo.play(cardList);
for (int i = 0; i < ret.size(); i++) {
System.out.println("第"+(i+1)+"个人的牌:"+ret.get(i));
}
System.out.println("剩下的牌:"+cardList);
}
}
运行结果:
买牌:[[1♥], [1♠], [1♣], [1♦], [2♥], [2♠], [2♣], [2♦], [3♥], [3♠], [3♣], [3♦], [4♥], [4♠], [4♣], [4♦], [5♥], [5♠], [5♣], [5♦], [6♥], [6♠], [6♣], [6♦], [7♥], [7♠], [7♣], [7♦], [8♥], [8♠], [8♣], [8♦], [9♥], [9♠], [9♣], [9♦], [10♥], [10♠], [10♣], [10♦], [11♥], [11♠], [11♣], [11♦], [12♥], [12♠], [12♣], [12♦], [13♥], [13♠], [13♣], [13♦]]
洗牌:[[2♥], [8♦], [4♦], [7♦], [1♦], [1♥], [11♣], [12♣], [4♠], [6♥], [5♥], [4♣], [10♠], [6♠], [11♥], [7♥], [8♠], [9♦], [10♦], [5♣], [9♣], [6♣], [3♠], [8♥], [13♦], [9♠], [2♦], [12♠], [7♣], [7♠], [13♠], [2♣], [2♠], [6♦], [1♣], [5♦], [3♦], [8♣], [13♥], [12♥], [12♦], [5♠], [4♥], [10♣], [3♣], [10♥], [11♦], [11♠], [1♠], [13♣], [3♥], [9♥]]
摸牌:
第1个人的牌:[[2♥], [7♦], [11♣], [6♥], [10♠]]
第2个人的牌:[[8♦], [1♦], [12♣], [5♥], [6♠]]
第3个人的牌:[[4♦], [1♥], [4♠], [4♣], [11♥]]
剩下的牌:[[7♥], [8♠], [9♦], [10♦], [5♣], [9♣], [6♣], [3♠], [8♥], [13♦], [9♠], [2♦], [12♠], [7♣], [7♠], [13♠], [2♣], [2♠], [6♦], [1♣], [5♦], [3♦], [8♣], [13♥], [12♥], [12♦], [5♠], [4♥], [10♣], [3♣], [10♥], [11♦], [11♠], [1♠], [13♣], [3♥], [9♥]]
三、练习
在长度为n(n > 1)的单链表上,设有头和尾两个引用,执行( )操作与链表的长度有关。
A.在单链表第一个元素前插入一个新元素
B.在单链表最后一个元素后插入一个新元素
C.删除单链表的第一个元素
D.删除单链表中的最后一个元素
A错误:头插不需要遍历链表,与链表长度无关
B错误:尾插不需要遍历链表,因为有一个引用指向了尾结点,可以直接插入
C错误:删除第一个节点也不需要遍历链表
D正确:删除最后一个节点之前,先要把倒数第二个节点找到,因为最后一个节点删除之后,需要将倒数第二个节点的next置为null 故需要遍历链表
因此选择D
下列关于链表的说法哪个是正确的( )
A.插入或者删除元素时,无需移动其他元素
B.数据在内存中一定是连续的
C.需要事先估计存储空间
D.可以随时访问表内的元素
A正确:链表中节点之间是通过next引用相互指向的,故插入或者删除元素时只需要修改几个引用的指向即可,不需要搬移元素
B错误:链表中的元素在内存中不一定连续,因为new的时候,会从堆上分配空间,具体分配出来的空间是否每次都连续,这个不一定
C错误:链表的空间不连续,插入时也不需要扩容之类的,因此不需要事先预估存储空间大小
D错误:链表不支持随机访问,需要访问任意位置元素时只能通过查找
因此选择A
在一个单链表中,q 的前一个节点为 p,删除 q 所指向节点时,以下代码正确的执行语句及次序是( )
① q.next=p.next;
② p.next=q.next;
A.①
B.②
p是前一个节点 q是后一个节点
要删除就是将q从链表中移出来,那就不要让p.next指向q了,指向q的后序节点即可,即p.next = q.next;
故应该选择B
在单链表的p节点之后插入s节点,正确的操作是( )
A.p.next = s; s.next = p.next;
B.p.next = s.next; p.next = s;
C.s.next = p.next; p.next = s;
D.p.next = s.next; p.next = s;
故选择C
下列数据结构中,不属于线性表的是( )
A.循环队列
B.链表
C.动态顺序表
D.二叉树
答案:D
解析:二叉树属于树形结构,不是线性的,队列,链表,顺序表都属于线性表
关于链表和顺序表间的区别,叙述错误的是( )
A.链表和顺序表都属于线性表
B.链表不能随机访问其中的某个元素,顺序表可以
C.链表能做的事,顺序表都可以完成,只是操作方法不同,效率不同
D.链表在进行插入和删除的时候,速度总是比顺序表快
答案:D
解析:链表的插入和删除不是所有情况下都比顺序表快,比如尾插尾删,顺序表的时间复杂度为O(1),并且如果是单链表,如果要在中间某个节点的前面插入/删除一个节点,则需要遍历。所以时间的快慢要分情况看待。
动态顺序表中,( )操作需要检查是否需要扩容
A.删除
B.插入
C.初始化
D.清空
答案:B
解析:插入操作需要考虑空间是否足够,如果不够需要先增容,再进行插入。
在长度为 n 的顺序表下标为 i 的位置前插入一个元素(1 ≤ i ≤ n+1),元素的移动次数为( )
A.n - i + 1
B.n - i
C.i
D.i - 1
答案:B
解析:顺序表插入元素,需要移动元素,这里需要把[i, n - 1]区间的元素全部向后移动一次,故移动的次数为n - 1 - i + 1
ArrayList和LinkList的描述,下面说法错误的是()
A.ArrayList和LinkedList都实现了List接口
B.ArrayList是可改变大小的数组,而LinkedList是双向链接串列
C.LinkedList不支持高效的随机元素访问
D.在LinkedList的中间插入或删除一个元素意味着这个列表中剩余的元素都会被移动;而在ArrayList的中间插入或删除一个元素的开销是固定的
A正确:ArrayList 和 LinkedList都是实现了List接口
B正确:ArrayList是动态类型顺序表,插入时当空间不足时会自动扩容
C正确:LinkedList底层是链表结构,链表不支持随机访问,如果要访问任意元素只能通过查找处理
D错误:LinkedList中插入或者删除元素,只需要修改几个引用的指向即可,不需要搬移愿意,时间复杂度O(1)。ArrayList任意位置插入和删除时才需要搬移,时间复杂度O(N)
================================记得三连哦=================================