Collection 下的子接口List接口,List接口实现的类有:ArrayList,Vector,LinkedList
1. List接口中的特有的方法:
**List集合中存储元素特点:有序可重复(有下标),有序指存进去的顺序与取出时相同 **
1)void add(int index, Object ele):在指定下标index位置插入ele元素
2)boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
3)Object get(int index):获取指定index位置的元素
4)int indexOf(Object obj):返回obj在集合中首次出现的位置 ,如果没有就返回-1
5)int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 ,如果没有就返回-1
6)Object remove(int index):移除指定index位置的元素,并返回此元素
注意:Collection中的remove是删除某个元素,这里是方法的重载而不是方法的重写 ,因为方法名一样,但形参类型不一样,在List中也可以按照对象去删除
7)Object set(int index, Object ele):设置指定index位置的元素为ele
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test02 {
public static void main(String[] args) {
//创建List接口对象
List mylist = new ArrayList();
//添加元素
mylist.add(11);//继承的父类中Collection中的add方法,默认是在集合末尾添加元素
mylist.add(22);
mylist.add(33);
mylist.add(11);
mylist.add("hello");
// 1.void add(int index, Object ele):在index位置(下标)插入ele元素
mylist.add(1,44);
// 2. Object get(int index):获取指定index位置的元素
Object o = mylist.get(1);
System.out.println(o);
// 3. int indexOf(Object obj):返回obj在集合中首次出现的位置 ,如果没有就返回-1
System.out.println(mylist.indexOf(11));
// 4. int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 ,如果没有就返回-1
System.out.println(mylist.lastIndexOf(11));
// 5.Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(mylist.remove(5));
// 6.Object set(int index, Object ele):设置指定index位置的元素为ele
System.out.println(mylist. set(2,"hei"));
// List遍历的方式
// 1)Iterator迭代器方式
Iterator it=mylist.iterator();
while(it.hasNext()) {
Object obj = it.next();
System.out.println(obj);
}
// 2)增强for循环
for( Object obj: mylist) {
System.out.println(obj);
}
// 3)普通的循环也可以,因为有索引
for(int i=0;i<mylist.size();i++) {
System.out.println(mylist.get(i));
}
/*44
0
4
hello
22
11
44
hei
33
11
11
44
hei
33
11
11
44
hei
33
11
*/
}
}
2. ArrayList
1. 集合的底层是一个Object数组
- 建议给定一个预估计的初始化容量,减少数组的扩容次数,是ArrayList集合较重要的优化策略
- 数组优点:检索效率比较高(每个元素占用空间大小相同,内存地址是连续的,若知道首元素内存地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高)
- 数组缺点: 随机增删元素效率较低,并且数组无法存储大数据量(因为很难找到一块巨大的连续的内存空间)
- 但向数组末尾添加元素,效率很高,不受影响
- 面试官会问:用那个集合较多?
ArrayList集合,因为向数组末尾添加元素,效率很高,不受影响,而检索/查找某个元素操作比较多
2. 构造方法
public static void main(String[] args) {
//1. 无参构造函数:默认初始化容量是10
List m1 = new ArrayList();
//2. 参数是int类型的构造函数:指定初始化容量;传入参数如果是大于等于0,则使用用户的参数初始化
List m2 = new ArrayList(20);
System.out.println(m1.size());//0
System.out.println(m2.size());//0
//说明集合的size()方法获取的是当前集合的元素的个数,不是集合的容量
//3. 参数是某个集合的构造函数
//创建一个HashSet集合
Collection c = new HashSet();
//给HashSet集合添加元素
c.add(10);
c.add(20);
c.add(30);
ArrayList m3 = new ArrayList(c);
//通过这个方法可以将HashSet集合转换为ArrayList集合
for(int i = 0;i<m3.size();i++){
System.out.println(m3.get(i));
}
3. LinkedList
1. 底层是双向链表
没有初始化容量,起初pre域和next域都为null
2. 优缺点:
-
链表优点:随机增删元素效率较高(因为链表上的元素在空间存储上内存地址不连续,增删元素不涉及到大量元素位移)
-
链表缺点:查询效率较低,因为不能通过数学表达式计算被查找元素的内存地址,每一次查找某个元素的时候都需要从头结点开始往下遍历
-
ArrayList将检索发挥到极致,而 LinkedList将随机增删发挥到极致
-
一般添加元素往末尾添加,所以ArrayList用的比LinkedList多
import java.util.LinkedList;
import java.util.List;
public class LinkedListTest {
public static void main(String[] args) {
List list = new LinkedList ();
list.add(1);
list.add(2);
list.add(3);
//遍历
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
//说明:LinkedList因为继承了List接口也有下标,但检索/查找效率低,因为要从头结点开始一个一个遍历
}
}
3. 面向接口编程
不论使用ArrayList集合还是LinkedList集合,我们都是面向List接口编程,调用的都是接口中的方法,仅仅是所对应的底层数据结构不同。
Vector(了解即可)
- 底层是一个数组,初始化容量是10,扩容是原容量的2倍,10, 20 ,40,而ArrayList扩容是原容量的1.5倍
- Vector 中的方法都是线程同步的,都带有synchronized关键字,是线程安全的
- 怎么将一个线程不安全的ArrayList集合转换为线程安全?
使用java.util.collections 集合工具类,区别于java.util.collection 集合接口
Vector vector =new Vector();
vector.add(1);
vector.add(2);
vector.add(3);
//遍历
for(int i=0;i<vector.size();i++) {
System.out.println(vector.get(i));
}
//将一个线程不安全的ArrayList集合转换为线程安全
List list = new LinkedList ();
Collections.synchronizedList(list);
泛型
- 像在源码中见到的
ArrayList<E>
,就是后面有个<E>
,就表示可以使用泛型机制 - 泛型只在编译阶段有效。只是给编译器看的
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test03 {
public static void main(String[] args) {
// List my = new ArrayList();
// //准备对象
// Cat c = new Cat();
// Bird b = new Bird();
// //将对象添加到集合中
// my.add(b);
// my.add(c);
// //遍历集合,取出每个Animal 让他move()
// Iterator it = my.iterator();
// while(it.hasNext()) {
// Object obj = it.next();
//// obj.move(); 或者 Animal obj = it.next(); 都是错误的,此时通过迭代器取出的是Object
//// Object中没有move()方法,要向下转型
// if(obj instanceof Animal) {
// Animal a = (Animal)obj;
// }
// }
//使用泛型,用来指定集合中存储的数据类型
List<Animal> my = new ArrayList<Animal>();
//my.add("hei"); 报错,不可以再存储String类型,使用泛型,使得集合中的数据类型统一,在此处ArrayList集合中只能存放Animal类型及其子集
Cat c = new Cat();
Bird b = new Bird();
my.add(b);
my.add(c);
//源码发现可以使用泛型 Iterator<E>
Iterator<Animal> it = my.iterator();
while(it.hasNext()) {
//那么使用迭代器取出的都是Animal类型,就不需要要向下转型,直接调用move()方法
Animal a = it.next();
a.move();
//遍历集合,取出Cat实现zhua(),取出Bird实现fly() 调用子类特有的方法,还是需要向下转型
if(a instanceof Cat) {
Cat x = (Cat)a;
x.zhua();
}
if(a instanceof Bird) {
Bird x = (Bird)a;
x.fly();
}
}
// 在JDK8后,有了类型自动判断:List<Animal> my = new ArrayList<>();后面的小括号不写类型
List<String> shu = new ArrayList<>();
shu.add("hewwww");
shu.add("adcf");
shu.add("abcdef");
Iterator<String> i = shu.iterator();
while(i.hasNext()) {//直接通过迭代器获取了String类型的数据
String s = i.next();
String s1 = s.substring(1);//String类的substring()方法,从下标为1处截取字符串
System.out.println(s1);
}
/*动物在移动
鸟在飞翔
动物在移动
猫咪抓老鼠
ewwww
dcf
bcdef
*/
}
}
class Animal{
public void move() {
System.out.println(" 动物在移动");
}
}
class Bird extends Animal {
public void fly() {
System.out.println("鸟在飞翔");
}
}
class Cat extends Animal{
public void zhua() {
System.out.println("猫咪抓老鼠");
}
}
优点:
集合中存储的元素类型统一,从集合中取出的元素类型是泛型指定的类型,不需要大量的“向下转型”
缺点:
导致集合中元素缺乏多样性
自定义泛型
public class Test05 {
public static void main(String[] args) {
//泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");
System.out.println(genericInteger.getKey());
System.out.println( genericString.getKey());
/*定义的泛型类,就一定要传入泛型类型实参么?并不是这样,在使用泛型的时候如果传入泛型实参,
* 则会根据传入的泛型实参做相应的限制,此时泛型才会起到本应起到的限制作用。
* 如果不传入泛型类型实参的话,在泛型类中使用泛型的方法或成员变量定义的类型可以为任何的类型。*/
Generic generic2 = new Generic(55.55);
Generic generic3 = new Generic(false);
System.out.println(generic2.getKey());
System.out.println(generic3.getKey());
}
}
//此处T可以随便写为任意标识符,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时(创建此泛型类型的对象时),必须指定T的具体类型
class Generic<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
增强for循环(又叫做foreach)
1.语法
for( 元素类型 变量名 :数组/集合){
System.out.println(变量名);
}
用于遍历很方便,但不适用于要使用下标的情况
int[] arr = {1,2,3};
//遍历数组
for(int i:arr) {
System.out.println(i);
}
//i代表数组中的每一个元素,类型一致
package 集合练习;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyList {
public static void main(String[] args) {
//创建List集合
List<String> list = new ArrayList<>();
//添加元素
list.add("asd");
list.add("asdfg");
list.add("asdhj");
//遍历1(用迭代器,可以使用泛型)
Iterator<String> it = list.iterator();
while(it.hasNext()) {
String string = it.next();
System.out.println(string);
}
//遍历2(针对于有下标的集合)
for(int i=0;i<list.size();i++) {
//System.out.println(i);最易忘记集合长度用方法size(),集合元素用get(下标)
System.out.println(list.get(i));
}
//遍历3(使用foreach)
for(String i:list) {
System.out.println(i);
}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现