复习
throw和throws的区别
- 位置:throw在方法内,throws在方法参数列表()后
- 后面写:throw后跟1个异常对象,throws后面跟多个异常类名
- 作用:throw是抛出异常对象的,throws只是声明可能要抛出异常类型
一、集合框架体系
数组:存放多个数据的容器
- 数组长度固定
- 数组只能存储同一种类型
- 数组用法单一,只能通过下标取值赋值
- 数组内元素可以重复
- 数组内元素有顺序(存值时顺序)
集合:存储多个数据的容器
- 集合长度不固定
- 集合可以存储不同类型
- 集合是一系列的类,可以创建对象,有丰富的方法可以操作数据
- 有些集合可以重复(List),有些集合不允许重复(Set);有些集合有序(List),有些集合是无序(HashSet),有些集合还会排序(TreeSet)
注:实线是继承,虚线是实现
- Collection是集合层次的父接口,定义了所有集合共性操作
- Collection有两个常用子接口:List(有序、重复集合)、Set(去重的集合)
- List接口有两个常用实现类:ArrayList(底层是数组)、LinkedList(底层是双向链表)
- Set接口有两个常用实现类:HashSet(底层是Hash表,无序去重)、TreeSet(底层是红黑二叉树,去重排序)
二、Collection、List介绍
- Collection父接口
- 定义了一部分集合的共性操作,并不会
- List是Collection的子接口,有序允许重复的集合
- 定义了一些方法,可以对位置进行精准控制
- 即可以按照下标插入,删除,查询,修改集合元素
- List是接口,不能直接用,常用使其子实现类,ArrayList和LinkedList
三、ArrayList
- 是List接口的实现类
- 底层是数组,大小“可变"
- 是不同步,即不保证线程安全
3.1方法演示1
- 初始化集合
ArrayList list = new ArrayList();
list.add(E e);向末尾添加元素,有序(插入顺序),允许重复,可以存储不同类型
list.add(index i,E e);向指定下标插入数据
Object o = list.get(index i);获得指定下标的元素
list.set(index i,E e);按照下标修改元素
Object old = list.remove(index i)按照下标删除元素,返回值是要删除的元素
演示示例:
public static void main(String[] args) {
ArrayList list = new ArrayList();
System.out.println("初始化:"+list);
//在集合中末尾添加元素
//有序(插入顺序),允许重复,允许存储不同类型
list.add(2);
list.add(4);
list.add("3");
list.add(27);
System.out.println("添加值后:"+list);
//指定下标插入元素
list.add(2,7);
System.out.println("中间插入后的集合列表:"+list);
//获得指定下标的元素,返回值类型是Object类
Object o = list.get(2);
System.out.println("获得指定下标元素:"+o);
//按照下标修改元素
list.set(2,19);
System.out.println("下标修改后:"+list);
//按照下标删除元素
Object old = list.remove(3);
System.out.println("要删除的元素:"+old);
System.out.println("删除后的集合:"+list);
}
3.2泛型
设置原因:
- 集合本身可以存储不同类型,但是有时候会想要使用一种类型,还要强制转换,但转换也有可能会出现失败
- 泛型样式:<>中就是要指定的类型
ArrayList<Integer> list1 = new ArrayList<>();
作用:
- 泛型来定义集合,在类名后,指定泛型类型,从而确定该集合只能存储指定类型
- 好处:减少类型转换(强转)
演示有泛型的增、删、改、查示例:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
//添加指定类型的集合
list.add(2);
list.add(4);
list.add(5);
System.out.println("添加值后:"+list);
//指定下标存放元素
list.add(3,6);
System.out.println("指向下标添加后:"+list);
//指定下标删除元素,
//由于设置了泛型,返回值就是指定类型而不是Object类型
Integer old = list.remove(4);
System.out.println("要删除的元素:"+old);
System.out.println("删除后的集合:"+list);
//指定下标修改元素
list.set(2,46);
System.out.println("指定下标修改后的集合:"+list);
}
以后,凡是用
3.3方法演示2
- list.addAll(Collection lis); 批量添加,将一个集合添加到用一个集合中
- list.removeAll(Collection lis); 异常当前集合中,存在于参数集合相同的元素
- list.isEmpty(); 判断是否为空,返回值是布尔型
- int size = list.size(); 存储元素的个数
- list.clear(); 清空集合
- list.contains(E e); 判断集合是否包含指定元素,返回值是布尔型
- Object[] o = list.toArray(); 转换为数组,虽然指定泛型,但返回值是Object
- 泛型集合转换为数组,以整型为例
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(44);
list.add(33);
list.add(22);
list.add(88);
System.out.println("list添加后的集合:"+list);
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(56);
list1.add(34);
list1.add(677);
list1.add(433);
System.out.println("添加后的list1"+list1);
//将list1中的所有元素添加到list,addAll() 批量添加
list.addAll(list1);
System.out.println("添加后的list:"+list);
//批量删除
list.removeAll(list1);
System.out.println("批量删除list1集合之后:"+list);
//判断是否为空
System.out.println(list.isEmpty());
//求集合中元素的个数
int size = list1.size();
System.out.println(size);
//判断集合中是否包含某个元素
System.out.println(list.contains(33));
//转换为数组
Object[] array1 = list1.toArray();
System.out.println("转换为Object类型数组:"+ Arrays.toString(array1));
// 转为规定数据类型的数组
//先创建整型数组
Integer[] integers = new Integer[list.size()];
Integer[] array = list.toArray(integers);
System.out.println("转换为数组:"+Arrays.toString(array1));
}
3.4迭代
//获得迭代器
Iterator<Integer> iterator = list.iterator();
//遍历迭代
while (iterator.hasNext()){ // 判断有无下一个元素
Integer next = iterator.next(); // 有就取出
System.out.println(next);
}
迭代器的简化写法:增强for循环,也叫foreach
// for(数据类型 变量 : 集合){} //遍历数组 int[] arr = {1,2,3,4}; for (int i : arr) { System.out.println(i); }
3.5底层原理[面试]
- ArrayList底层是数组实现的
- 起始容量(数组长度)默认是10
- 刚new完创建的空集合的,容量是0
- 当第一次加入元素的时候,数组 扩容成10
- 当元素放满10个时,当加入第十一个时候会**触发扩容,扩容为原来的1.5倍(**通过>>1右移一位算出来)
- 扩容1.5倍后,再把原数组的元素拷贝到新数组
代码展示扩容原理:
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
使用上效率问题
- ArrayList进行元素的查询,修改速度快
- 进行元素的插入和删除速度慢
原因
- 数组在内存是连续空间,有下标
- 所以,通过下标直接定位找到元素,改变该位置元素(查询和更新快)
- 也是因为内存空间原因,插入或者删除一个数据时,从插入/删除位置开始后续的元素需要后移/前移一位
四、LinkedList
4.1方法演示
LinkedList也是List实现类,大部分方法和ArrayList一模一样用法
public static void main(String[] args) {
LinkedList<Integer> integers = new LinkedList<>();
integers.add(97);
integers.add(67);
integers.add(923);
integers.add(97);
LinkedList<Integer> integers1 = new LinkedList<>();
integers1.add(11);
integers1.add(22);
integers1.add(33);
integers1.add(44);
//批量添加
integers.addAll(integers1);
System.out.println("批量添加后的:"+integers);
System.out.println("-----------------");
//批量删除
integers.removeAll(integers1);
System.out.println("批量删除后:"+integers);
//修改
integers.set(3,567);
System.out.println("修改后:"+integers);
//迭代
for (Integer integer : integers) {
System.out.println(integer);
}
}
4.2特殊方法
除了基本的方法与ArrayList一样之外,LinkedList还提供一些比较特殊的操作头尾方法
- getFirst getLast ----->获得头/尾元素
- removeFirst removeLast ------>删除头/尾元素
- addFirst addLast ------>在头/尾添加元素
4.3底层原理
LinkedList底层是双向链表
- 链表在内存不连续
- 通过链域里面记录上一个/下一个元素的位置
当通过找下一个元素时并不是直接定位的,是从头或者尾开始一个一个遍历查找对应的元素,也正是因为如此,所以提供了专门操作头尾的方法,可以直接定位到方便操作
又因为这个空间数据结构问题,导致了一些特性
- LinkedList查询,更新数据慢
- LinkedList插入,删除快
- 因为插入/删除元素改变的知识链域的记录信息,没有改变其他元素位置
练习
//用集合完成,编写一个模拟KTV点歌系统的程序。在程序中,输入0代表添加歌曲,输入1代表将所选歌曲置顶,输 入2代表将所选歌曲提前一位,输入3代表退出该系统
public static void main(String[] args) {
System.out.println("-----------------------欢迎来到点歌系统---------------------");
System.out.println("0.添加歌曲至列表");
System.out.println("1.将歌曲置顶");
System.out.println("2.将歌曲前移一位");
System.out.println("3.退出");
LinkedList<String> songs = new LinkedList<>();
songs.add("伶人");
songs.add("勇气");
songs.add("红豆");
songs.add("胡广生");
songs.add("岁岁");
System.out.println("初始化歌曲列表:"+songs);
while (true){
System.out.println();
Scanner scanner = new Scanner(System.in);
System.out.print("请选择要执行的操作序号:");
int select = scanner.nextInt();
switch (select){
case 0:
// addSongs添加上歌曲
System.out.print("请输入要添加的歌曲名称:");
String addSong = scanner.next();
if (songs.contains(addSong)){
System.out.println("已经有该歌曲"+addSong);
break;
}else {
songs.add(addSong);
System.out.println("已添加歌曲:"+addSong);
System.out.println("当前歌曲列表:"+songs);
break;
}
case 1:
// firstSongs添加上歌曲
System.out.print("请输入要置顶的歌曲名称:");
String firstSong = scanner.next();
if (songs.contains(firstSong)){
songs.addFirst(firstSong);
System.out.println("已将歌曲"+firstSong+"置顶");
System.out.println("当前歌曲列表:"+songs);
break;
} else {
System.out.println("不存在该歌曲");
break;
}
case 2:
// removeSongs添加上歌曲
System.out.print("请输入要提前一位的歌曲名称:");
String removeSong = scanner.next();
if (songs.contains(removeSong)){
int index = songs.indexOf(removeSong);
if (index == 0){
System.out.println("该歌曲已置顶,无法前进一位");
break;
} else {
songs.remove(removeSong);
songs.add(index-1,removeSong);
System.out.println("已将歌曲"+removeSong+"提前一位");
System.out.println("当前歌曲列表:"+songs);
break;
}
} else {
System.out.println("不存在该歌曲");
break;
}
case 3:
System.out.println("-----------------退出---------------------");
System.out.println("您已退出系统");
exit(0);
break;
default:
System.out.println("请输入正确格式!");
break;
}
}
}