一、异常
1、认识异常
异常代表程序出现的问题。
Java的异常体系:Java.lang.Throwable之下的异常类。
Throwable(父类)-->Error/(Exception-->RuntimeException/其他异常)
Error:系统级别错误(属于严重问题),系统一旦出现问题,sun公司会把这些问题封装成Error对象给出来,如:内存溢出、栈溢出等。
Exception:异常,代表程序可能出现的问题,程序员通常会用Exception以及它的孩子来封装程序出现的问题。分为运行时异常和编译时异常。
- 运行时异常:RuntimeException及其子类,编译阶段不会出现错误提醒,程序运行过程中出现的问题,如:空指针异常、数组索引越界异常等。
- 编译时异常:编译阶段出现错误提醒的问题,如:语法错误、类型转换错误等。
异常的基本处理:
- 抛出异常(throws):在方法上使用throw关键字,可以将方法内部出现的异常抛出去给调用者处理。方法() throws 异常1,异常2{ }//或遇到两个异常只抛出父异常Exception。
- 捕获异常(try...catch):直接捕获程序出现的异常。try{ } catch(异常类型1 变量){ } catch(异常类型2 变量){ }
异常的作用:
- 异常是用来定位程序bug的关键信息。
- 可以作为方法内部的一种特殊返回值,以便通知上层调用者,方法的执行问题。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExceptionDemo1 {
public static void main(String[] args){
//认识异常的体系,搞清楚异常的基本作用
//show();
try {
//监视代码,出现异常,会被catch拦截住这个异常
show1();
} catch (Exception e) {
e.printStackTrace();//打印异常信息
}
}
//定义一个方法认识编译时异常
public static void show1() throws Exception{
System.out.println("show1方法执行了...");
//编译异常:编译阶段报错,编译不通过
String str="2024-07-09 11:12:13";
//把字符串时间解析成Java的一个日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);//编译时异常,提醒程序员这里的程序很容易出错,请注意
System.out.println(date);
InputStream is=new FileInputStream("D:/meinv.png");
System.out.println("show1方法结束了");
}
//定义一个方法认识运行时异常
public static void show(){
System.out.println("show方法执行了...");
//运行时异常特点:编译阶段不报错,运行时出现的异常,继承自RuntimeException
int[] arr = {10,20,30};
//int res = arr[3];
//System.out.println(res);//输出:运行时异常,数组越界异常,ArrayIndexOutOfBoundsException
//System.out.println(10/0);//输出:运行时异常,除数不能为0,ArithmeticException
//空指针异常
String str = null;
System.out.println(str.length());//输出:运行时异常,空指针异常,NullPointerException
System.out.println("show方法结束了");
}
}
public class ExceptionDemo2 {
public static void main(String[] args) {
//搞清楚异常的作用
System.out.println("程序开始执行");
try {
System.out.println(div(10,0));
System.out.println("底层方法执行成功了");
} catch (Exception e) {
e.printStackTrace();
System.out.println("底层方法执行失败了");
}
System.out.println("程序结束");
}
//求两个数的除的结果,并返回这个结果
public static int div(int a,int b) throws Exception {
if(b==0){
System.out.println("除数不能为0");
//可以返回一个异常给上层调用者,返回的异常还能告知上层底层是执行成功了还是执行失败了
throw new Exception("除数不能为0");
}
int result=a/b;
return result;
}
}
2、自定义异常
Java无法为这个世界上全部的问题都提供异常类来代表,如果企业自己的某种问题想通过异常来表示,以便用异常来管理该问题,那就需要自己来定义异常类。
1、自定义编译时异常:定义一个异常类继承Exception类,并重写构造方法,通过throw new 异常类(xxx) 创建异常对象并抛出。
2、自定义运行时异常:定义一个异常类继承RuntimeException类,并重写构造方法,通过throw new 异常类(xxx) 创建异常对象并抛出。
//自定义编译时异常
//1、继承Exception做爸爸
//2、重写Exception的构造器
//3、哪里需要用这个异常返回,哪里就throw这个异常
public class AgeIllegalException extends Exception{
public AgeIllegalException(){
}
public AgeIllegalException(String message){
super(message);
}
}
public class ExceptionDemo3 {
public static void main(String[] args) {
//认识自定义异常
System.out.println("程序开始执行");
try {
saveAge(300);
System.out.println("年龄保存成功");
} catch (AgeIllegalException e) {
e.printStackTrace();
System.out.println("年龄非法,保存失败");
}
System.out.println("程序结束");
}
//需求:我们公司的系统只要收到了年龄小于1岁或者大于200岁就是一个年龄非法异常
public static void saveAge(int age) throws AgeIllegalException {
if(age<1||age>200){
//年龄非法,抛出去一个异常返回
throw new AgeIllegalException("年龄非法 age 不能低于1岁不能高于200岁");
}else{
System.out.println("年龄合法");
System.out.println("保存年龄"+age);
}
}
}
//自定义运行时时异常
//1、继承RuntimeException做爸爸
//2、重写RuntimeException的构造器
//3、哪里需要用这个异常返回,哪里就throw这个异常
public class AgeIllegalRuntimeException extends RuntimeException{
public AgeIllegalRuntimeException(){
}
public AgeIllegalRuntimeException(String message){
super(message);
}
}
public class ExceptionDemo4 {
public static void main(String[] args) {
//认识自定义异常-运行时异常
System.out.println("程序开始执行");
try {
saveAge(300);
System.out.println("年龄保存成功");
} catch (Exception e) {
e.printStackTrace();
System.out.println("年龄非法,保存失败");
}
System.out.println("程序结束");
}
//需求:我们公司的系统只要收到了年龄小于1岁或者大于200岁就是一个年龄非法异常
public static void saveAge(int age){
if(age<1||age>200){
//年龄非法,抛出去一个异常返回
throw new AgeIllegalRuntimeException("年龄非法 age 不能低于1岁不能高于200岁");
}else{
System.out.println("年龄合法");
System.out.println("保存年龄"+age);
}
}
}
如果想要表达强烈的提醒(他人易犯的错误),就用编译时异常(少用),若程序员自己能避免(别人不易犯错),就使用运行时异常(多用)。
3、异常的处理方案
1、底层异常层层往上抛出,最外层捕获异常,记录下异常信息,并响应适合用户观看的信息进行提升。
2、最外层获取异常后,尝试重新修复。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ExceptionDemo5 {
public static void main(String[] args) {
//掌握异常的处理方案1:底层异常都抛出去给最外层调用者,最外层捕获异常,记录异常,响应合适信息给用户观看
System.out.println("程序开始执行");
try {
show();
System.out.println("这次操作成功了");
} catch (Exception e) {
e.printStackTrace();
System.out.println("这次操作失败了");
}
System.out.println("程序结束");
}
public static void show() throws Exception {
//编译异常:编译阶段报错,编译不通过
String str="2024-07-09 11:12:13";
//把字符串时间解析成Java的一个日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = sdf.parse(str);//编译时异常,提醒程序员这里的程序很容易出错,请注意
System.out.println(date);
InputStream is=new FileInputStream("D:/meinv.png");
}
}
import java.util.Scanner;
public class ExceptionDemo6 {
public static void main(String[] args) {
//掌握异常的处理方案2:捕获异常对象,尝试重新修复
//接收用户的一个定价
System.out.println("程序开始执行");
while (true) {
try {
double price = userInputPrice();
System.out.println("用户成功设置了商品定价"+price);
break;
} catch (Exception e) {
//e.printStackTrace();
System.out.println("用户输入价格有误,请重新输入");
}
}
System.out.println("程序结束");
}
public static double userInputPrice(){
Scanner sc = new Scanner(System.in);
System.out.println("请输入商品定价");
double price = sc.nextDouble();
return price;
}
}
二、泛型(Generic)
1、认识泛型
定义类、接口、方法时,同时声明了一个或者多个类型变量(如:<E>),称为泛型类、泛型接口、泛型方法,它们统称为泛型。
作用:泛型提供了在编译阶段约束所能操作的数据类型,并自动进行检查的能力,这样可以避免强制类型转换,及其可能出现的异常。
泛型的本质:把具体的数据类型作为参数传给类型变量。
2、泛型类
基本语法:修饰符 class 类名<类型变量,类型变量,...>{ }
应用场景:在工具类中,经常会有一些方法需要处理不同类型的对象,如集合操作、数据转换等,这时可以使用泛型方法来增强工具类的通用性。
注意:类型变量用大写字母,如:T、E、K、V等。
可以控制类接收的类型变量,由于支持多个类型变量,故需注意类型变量的顺序,如:<T,E>、<E,T>之类的。
import java.util.ArrayList;
public class GenericDemo1 {
public static void main(String[] args) {
//认识泛型,搞清楚使用泛型的好处
ArrayList<String> list = new ArrayList<>();
list.add("abc");
// list.add(123);
// list.add(true);
// list.add(new Object());
//获取数据
for(int i = 0; i < list.size(); i++){
String rs = list.get(i);
System.out.println(rs);
// Object rs = list.get(i);
// //把数据转型处理
// String str = (String) rs;
// System.out.println(str);
}
}
}
import java.util.ArrayList;
//自定义泛型类
public class MyArrayList<E> {
private ArrayList list=new ArrayList();
public boolean add(E e){
list.add(e);
return true;
}
public boolean remove(E e){
return list.remove(e);
}
public String toString(){
return list.toString();
}
}
public class GenericDemo2 {
public static void main(String[] args) {
//学会自定义泛型类
//模拟ArrayList集合自定义一个集合MyArrayList
MyArrayList<String> list = new MyArrayList<>();
list.add("abc");
list.add("bcd");
list.add("cde");
list.add("def");
System.out.println(list.remove("abc"));
System.out.println(list);
}
}
3、泛型接口
基本语法:修饰符 interface 接口名<类型变量,类型变量,...>{ }
4、泛型方法、通配符、上下限
1、泛型方法:定义一个方法,在方法定义中添加类型变量,然后在方法中使用类型变量。
泛型方法作用:泛型方法可以避免强制类型转换,在编译时就能够报错,同时能够确保方法接收参数的多样性,提升方法的复用性。
import demo3genericity.Student;
public class GenericDemo4 {
public static void main(String[] args)
{
//学会定义泛型方法,搞清楚作用
//打印任意数组的内容
String[] names = {"张三","李四","王五"};
printArray(names);
Student[] stus=new Student[3];
printArray(stus);
Student max = getMax(stus);
String max1 = getMax(names);
}
public static <T> void printArray(T[] names)
{
for (int i = 0; i < names.length; i++) {
System.out.println(names[i]);
}
}
public static <T> T getMax(T[] names)
{
return null;
}
}
2、通配符与上下限
通配符是泛型类型的占位符,通配符可以接受任意类型,如:List<?>、List<? extends Car>、List<? super BYD>等。
其中泛型上限:? extends Car表示只能接受Car及其子类
其中泛型下限:? super BYD表示只能接受BYD及其父类
如果通配符是?,则能够接收所有的类型变量。
public class Car {
}
public class LX extends Car{
}
public class BYD extends Car{
}
public class Xiaomi extends Car{
}
import java.util.ArrayList;
public class GenericDemo5 {
public static void main(String[] args)
{
//理解通配符和上下限
//开发一个极品飞车游戏
ArrayList<Xiaomi> xiaomis = new ArrayList<>();
xiaomis.add(new Xiaomi());
xiaomis.add(new Xiaomi());
xiaomis.add(new Xiaomi());
go(xiaomis);
ArrayList<BYD> byds = new ArrayList<>();
byds.add(new BYD());
byds.add(new BYD());
go(byds);
// ArrayList<Dog> dogs = new ArrayList<>();
// dogs.add(new Dog());
// dogs.add(new Dog());
// go(dogs);
}
//虽然小米和比亚迪是Car的子类,但是ArrayList<Xiaomi> 和 ArrayList<BYD> 和 ArrayList<Car>没有任何关系
public static void go(ArrayList<? extends Car> cars){
}
}
5、泛型支持的类型
泛型不支持基本数据类型,如:int、char、boolean等,但可以支持对象类型(引用数据类型)。
解释:泛型工作在编译阶段,编译阶段结束后不工作了,故泛型在编译后会被擦除,所有类型都会恢复成Object类型。
而Object类型只能接收引用类型(自定义类型或包装类),如:Integar、Character等包装类(把基本数据类型的数据包装成对象的类型)。
包装类:Integar、Character、Boolean、Byte、Short、Long、Float、Double
为什么要有包装类,包装类有哪些?
- 为了万物皆对象,并且泛型和集合都不支持基本类型,支持包装类
- 8种,int->Integar、char->Character,其他的都是首字母大写
包装类提供了哪些常用的功能?
- 可以把基本类型的数据转换成字符串类型
- 可以把字符串类型的数值转换成数值本身对应的真实数据类型
自动装箱:基本数据类型可以自动转换为包装类型。
自动拆箱:包装类型可以自动转换为基本数据类型。
import demo2genericity.MyArrayList;
import java.util.ArrayList;
public class GenericDemo6 {
public static void main(String[] args)
{
//搞清楚泛型和集合不支持基本数据类型,只能支持对象类型(引用数据类型)
//ArrayList<int> list=new ArrayList<>();
//泛型擦除:泛型工作在编译阶段,等编译后泛型就没用了,所以泛型在编译后都会被擦除,所以类型会恢复成Object类型
//把基本数据类型变成包装类对象
//手工包装:
//Integer i=new Integer(10);//过时
Integer it1=Integer.valueOf(130);//推荐的
Integer it2=Integer.valueOf(130);
System.out.println(it1==it2);
//自动包装:基本数据类型的数据可以直接变成包装对象的数据,不需要额外做任何事情
Integer it3=130;
Integer it4=130;
System.out.println(it3==it4);
//自动拆箱:把包装类型的对象直接给基本类型的数据
int i=it3;
System.out.println(i);
ArrayList<Integer> list=new ArrayList<>();
list.add(10);//自动装箱
list.add(20);
int rs=list.get(0);//自动拆箱
//包装类新增的功能
//1、可以把基本类型的数据转换成字符串
int j=23;
String rs1=Integer.toString(j);//"23"
System.out.println(rs1+1);//231
Integer i2=j;
String rs2=i2.toString();//"23"
System.out.println(rs2+1);//231
String rs3=j+"";
System.out.println(rs3+1);//231
//2、把字符串数值转换成对应的基本数据类型(很有用)
String str="123";
//int rs4=Integer.parseInt(str);//123
int rs4=Integer.valueOf(str);//123
System.out.println(rs4+1);//124
}
}
三、集合框架
1、认识集合
- 集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。
两类集合:单列集合、双列集合。
- Collection代表单列集合,每个元素(数据)只包含一个值。
- Map代表双列集合,每个元素包含两个值(键值对)。
- Collection<E>、List<E>、Set<E>和Map<K,V>只是接口,其子类都是其实现类。
Collection集合特点
- List系列集合:添加的元素是有序、可重复、有索引的。
- ArrayList、LinkedList:有序、可重复、有索引。
- Set系列集合:添加的元素是无序、不重复、无索引的。
- HashSet:无序、不重复、无索引。
- LinkedHashSet:有序、不重复、无索引。
- TreeSet:按照大小默认升序排序、不重复、无索引。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class CollectionDemo1 {
public static void main(String[] args) {
//搞清楚Collection集合的整体特点
//1、List家族的集合:有序、可重复、有索引
List<String> list=new ArrayList<>();
list.add("Java");
list.add("Java");
list.add("C");
list.add("C++");
System.out.println(list);//顺序和添加顺序一致
String rs=list.get(0);
System.out.println(rs);
//2、Set家族的集合:无序、不可重复、无索引
Set<String> set=new HashSet<>();
set.add("鸿蒙");
set.add("Java");
set.add("Java");
set.add("C");
set.add("C++");
System.out.println(set);
}
}
2、Collection集合
1、常用功能
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
Collection方法名 | 说明 |
public boolean add(E e) | 把元素e添加到集合中,并返回true |
public void clear() | 清空集合中的元素 |
public boolean remove(E e) | 删除集合中元素e,并返回true |
public boolean contains(E e) | 判断集合中是否包含元素e,返回true |
public boolean isEmpty() | 判断集合是否为空,返回true |
public int size() | 返回集合中元素的个数 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionDemo2 {
public static void main(String[] args) {
//搞清楚Collection提供的通用集合功能
Collection<String> c=new ArrayList<>();
//添加元素
c.add("Java");
c.add("C");
c.add("C++");
System.out.println(c);
//获取集合的元素个数
int size=c.size();
System.out.println(size);
//删除集合元素
c.remove("C");
System.out.println(c);
//判断集合是否为空
boolean empty=c.isEmpty();
System.out.println(empty);
//清空集合
// c.clear();
// System.out.println(c);
//判断集合中是否存在某个数据
boolean b=c.contains("Java");
System.out.println(b);
//把集合转换成数组
Object[] arr=c.toArray();
System.out.println(Arrays.toString(arr));
String[] arr2=c.toArray(String[]::new);
}
}
2、三种遍历方式
Collection的遍历方式之一:迭代器遍历:Iterator e=collection.iterator();
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTraversalDemo3 {
public static void main(String[] args) {
//掌握Collection的遍历方式一:迭代器遍历
Collection<String> names = new ArrayList<>();
names.add("林青霞");
names.add("张曼玉");
names.add("王祖贤");
names.add("柳岩");
System.out.println(names);
//1、得到这个集合的迭代器对象
Iterator<String> it = names.iterator();
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
// System.out.println(it.next());
//2、使用一个while循环来遍历
while(it.hasNext()){
String name = it.next();
System.out.println(name);
}
}
}
Collection的遍历方式二:增强for循环:for(元素数据类型 变量名:数组或者集合名){ }
- 增强for可以用来遍历集合或者数组。
- 增强for遍历集合,本质就是迭代器遍历集合的简化写法。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionTraversalDemo4 {
public static void main(String[] args) {
//掌握Collection的遍历方式二:增强for
Collection<String> names = new ArrayList<>();
names.add("林青霞");
names.add("张曼玉");
names.add("王祖贤");
names.add("柳岩");
System.out.println(names);
for (String name : names) {
System.out.println(name);
}
String[] users = {"张三丰","张无忌","张翠山","张天师"};
for (String user : users) {
System.out.println(user);
}
}
}
Collection集合的遍历方式三:Lambda表达式:集合对象名.forEach(E e)
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class CollectionTraversalDemo5 {
public static void main(String[] args) {
//掌握Collection的遍历方式三:lambda
Collection<String> names = new ArrayList<>();
names.add("林青霞");
names.add("张曼玉");
names.add("王祖贤");
names.add("柳岩");
System.out.println(names);
// names.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s);
// }
// });
// names.forEach(s-> System.out.println(s));
names.forEach(System.out::println);
}
}
3、三种遍历方式的区别
遍历集合的同时又存在增删集合元素的行为时可能出现业务异常,这种现象被称之为并发修改异常问题。
解决并发修改异常问题的方案
1、如果集合支持索引,可以使用for循环遍历,没删除数据后做i--,或者可以倒着遍历。
2、可以使用迭代器遍历,并用迭代器提供的删除方法删除数据。
注意:增强for循环/Lambda遍历均不能解决并发修改异常问题,因此它们只适合做数据的遍历,不适合同时做增删操作。
import java.util.ArrayList;
import java.util.Iterator;
public class CollectionTraversalDemo6 {
public static void main(String[] args) {
//认识并发修改问题,搞清楚每种遍历的区别
ArrayList<String> list = new ArrayList<>();
list.add("Java入门");
list.add("宁夏枸杞");
list.add("黑枸杞");
list.add("人字拖");
list.add("特级枸杞");
list.add("枸杞子");
list.add("西洋参");
System.out.println(list);
//需求1:删除全部枸杞
// for (int i = 0; i < list.size(); i++) {
// String rs = list.get(i);
// if(rs.contains("枸杞")){
// list.remove(rs);
// }
// }
// System.out.println(list);//出现并发修改异常问题
//[Java入门, 宁夏枸杞, 黑枸杞, 人字拖, 特级枸杞, 枸杞子, 西洋参]
//[Java入门, 黑枸杞, 人字拖, 枸杞子, 西洋参]
for (int i = 0; i < list.size(); i++) {
String rs = list.get(i);
if(rs.contains("枸杞")){
list.remove(rs);
i--;//解决方案1:删除数据后做一步i--操作
}
}
System.out.println(list);
ArrayList<String> list2 = new ArrayList<>();
list2.add("Java入门");
list2.add("宁夏枸杞");
list2.add("黑枸杞");
list2.add("人字拖");
list2.add("特级枸杞");
list2.add("枸杞子");
list2.add("西洋参");
System.out.println(list2);
//需求1、删除全部枸杞
//解决方案2:倒着遍历并删除(前提是支持索引)
for (int i = list2.size()-1; i >= 0; i--) {
String rs = list2.get(i);
if(rs.contains("枸杞")){
list2.remove(rs);
}
}
System.out.println(list2);
ArrayList<String> list3 = new ArrayList<>();
list3.add("Java入门");
list3.add("宁夏枸杞");
list3.add("黑枸杞");
list3.add("人字拖");
list3.add("特级枸杞");
list3.add("枸杞子");
list3.add("西洋参");
System.out.println(list3);
//需求1、删除全部枸杞
//方案一:迭代器遍历并删除默认也存在并发修改异常问题
//可以解决:使用迭代器自己的方法来删除
Iterator<String> it = list3.iterator();
while(it.hasNext()){
String rs = it.next();
if(rs.contains("枸杞")){
it.remove();
}
}
System.out.println(list3);
ArrayList<String> list4 = new ArrayList<>();
list4.add("Java入门");
list4.add("宁夏枸杞");
list4.add("黑枸杞");
list4.add("人字拖");
list4.add("特级枸杞");
list4.add("枸杞子");
list4.add("西洋参");
System.out.println(list4);
//需求1、删除全部枸杞
//方案二和三:用增强for还有Lambda(都没有办法解决并发修改异常问题)
//结论:增强for和Lambda只适合做遍历,不适合做遍历并修改操作
// for (String rs : list4) {
// if(rs.contains("枸杞")){
// list4.remove(rs);
// }
// }
// list4.forEach(rs -> {
// if(rs.contains("枸杞")){
// list4.remove(rs);
// }
// });
System.out.println(list4);
}
}
3、List集合
1、List的特点、特有功能
List系列集合特点:有序,可重复,有索引。
- ArrayList:有序,可重复,有索引。
- LinkedList:有序,可重复,有索引。
底层实现不同,适合的场景不同!
- List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
方法名称 | 说明 |
void add(int index,E e) | 在指定位置插入元素e |
E remove(int index) | 删除指定位置的元素e,返回被删除的元素 |
E set(int index,E e) | 修改指定位置的元素e,返回被修改的元素 |
E get(int index) | 获取指定位置的元素e |
List集合支持的遍历方式:
1、for循环(因为List集合有索引)
2、迭代器
3、增强for循环
4、Lambda表达式
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo1 {
public static void main(String[] args)
{
//掌握List系列集合独有的功能
List<String> names = new ArrayList<>();
//添加数据
names.add("张三");
names.add("李四");
names.add("王五");
System.out.println(names);
//给第三个位置插入一个数据
names.add(2,"赵六");
System.out.println(names);
//删除李四
names.remove("李四");
System.out.println(names);
//把王五修改成:金毛
names.set(2,"金毛");
System.out.println(names);
//获取张三
String name=names.get(0);
System.out.println(name);
System.out.println("--------------------------");
//1、for循环
for (int i = 0; i < names.size(); i++) {
String name1 = names.get(i);
System.out.println(name1);
}
//2、迭代器
Iterator<String> it = names.iterator();
while(it.hasNext()){
String name2 = it.next();
System.out.println(name2);
}
//3、增强for循环
for (String name3 : names) {
System.out.println(name3);
}
//Lambda
names.forEach(name4 -> System.out.println(name4));
}
}
2、ArrayList底层原理
ArrayList和LinkedList的区别:底层采用的数据结构(存储、组织数据的方式)不同,应用场景不同。
- ArrayList底层是基于数组存储数据的。
- LinkedList底层是基于链表存储数据的。
基于数组存储数据:内存中连续的一块区域A,根据索引查询数据快(查询数据通过地址值和索引定位,查询任意数据耗时相同),但是增删数据效率低(可能需要把后面很多的数据进行前移)。
注意:ArrayList集合new对象的时候为空数组,第一次添加数据时,会创建一个默认大小为10的数组,每当集合元素个数超过数组大小时,会扩容成原来的1.5倍。
3、LinkedList底层原理
- LinkedList底层是基于双链表存储数据的。
链表的特点:
链表中的数据是一个一个独立的结点组成的,结点在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
- 链表的特点1:查询慢,无论查询哪个数据都要从头开始找。
- 链表的特点2:链表增删相对快。
基于双链表存储数据:内存中不连续的一块区域B,每个结点包含数值和上、下一个结点的地址,查询数据慢,但是增删数据效率高,同时双链表相对于单链表对首尾元素进行增删改查的速度是极快的。
LinkedList新增了:很多首尾操作的特有方法。
LinkedList的应用场景:可以用来设计队列(只在首尾增删数据),可以用来设计栈(只在栈顶增删数据)。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ListDemo2 {
public static void main(String[] args)
{
//用LinkedList做一个队列对象
LinkedList<String> queue = new LinkedList<>();
//入队
queue.addLast("张三");
queue.addLast("李四");
queue.addLast("王五");
System.out.println(queue);
//出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
System.out.println("-----------------------------------");
//做一个栈
LinkedList<String> stack = new LinkedList<>();
//压栈
stack.push("第一颗子弹");
stack.push("第二颗子弹");
stack.push("第三颗子弹");
System.out.println(stack);
//出栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}