目录
34.7.1 字符输入转换流:InputStreamReader
34.7.2 字符输入转换流:OutputStreamWriter
37.1.3 多线程的实现方案三:利用Callable、FutureTask接口实现
38.2 ScheduledExecutorService定时器
21 集合
之前我们已经使用过数组和集合了,在这里回顾一下:
数组 | 集合 | |
定义后 | 类型确定、长度固定 | 大小,类型不固定 |
适用场景 | 数据个数和类型确定的场景 | 适合元素个数不能确定,且需要做元素的增删操作的场景。 |
功能 | 单一 | 强大且种类丰富 |
储存 | 基本类型和引用类型的数据 | 只能存储引用数据类型的数据 |
21.1 集合的分类
集合可分为Collection单列集合与Map双列集合
Collection单列集合,每个元素(数据)只包含一个值。
Map双列集合,每个元素包含两个值(键值对)。
21.1.1 Collection集合
定义:Collection是单列集合的祖宗接口,它的功能是全部单列集合都可以继承使用的。
分类
Collection (接口) |
|||
List (接口) |
Set (接口) |
||
ArrayList (实现类) |
LinkedList (实现类) |
HashSet (实现类) |
TreeSet (实现类) |
LinkedHashSet (实现类) |
特点
List系列集合:
添加的元素是有序、可重复、有索引。
ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
HashSet: 无序、不重复、无索引;
LinkedHashSet: 有序、不重复、无索引。
TreeSet:按照大小默认升序排序、不重复、无索引。
21.2 泛型简述
集合都是泛型的形式
作用:可以在编译阶段约束集合只能操作某种数据类型
写法:
Collection<String> lists = new ArrayList<String>();
Collection<String> lists = new ArrayList<>(); // JDK 1.7开始后面的泛型类型申明可以省略不写
注意:集合和泛型都只能支持引用数据类型,不支持基本数据类型,所以集合中存储的元素都认为是对象。
如果集合中要存储基本类型的数据,需要变为引用类型。
21.3 Collection集合常用API
方法名称 |
作用 |
public boolean add(E e) |
把给定的对象添加到当前集合中 |
public void clear() |
清空集合中所有的元素 |
public boolean remove(E e) |
把给定的对象在当前集合中删除 |
public boolean contains(Object obj) |
判断当前集合中是否包含给定的对象 |
public boolean isEmpty() |
判断当前集合是否为空 |
public int size() |
返回集合中元素的个数。 |
public Object[] toArray() |
把集合中的元素,存储到数组中 |
//用多态定义一个集合
Collection<String> list = new ArrayList<>();
//把集合转换成数组
//虽然用泛型规定了只能存储String类型,但是可以通过其他技术强制存储别的类型,所以用Object
Object arr[] = list.toArray();
System.out.println("数组:" + Arrays.toString(arr));
System.out.println(list);//结果是一样的
21.4 Collection集合遍历
21.4.1 迭代器
什么是遍历?
遍历就是一个一个的把容器中的元素访问一遍。
迭代器是集合的专用的遍历方式。
迭代器在Java中的代表是Iterator
Collection集合获取迭代器
方法名称 |
作用 |
Iterator<E> iterator() |
返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引 |
Iterator中的常用方法
方法名称 |
作用 |
boolean hasNext() |
询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() |
获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意:防止取出越界。 |
小案例题
一个集合中存储着一些数据,将这些数据都取出来
Collection<String> c1 = new ArrayList<>();
// 1.得到当前集合的迭代器对象
Iterator it = c1.iterator();//迭代器先取出再移位
while(it.hasNext()){//当当前位置有元素存在时
System.out.println(it.next());//取出当前位置的元素,之后移位
}
注意:
迭代器如果取元素越界会出现NoSuchElementException异常
21.4.2 增强for循环
书写格式:
for(元素数据类型 变量名 : 数组或者Collection集合) {
}
【在IDEA中可以使用: 数组或者Collection集合.for 这个快捷键】
作用范围:既可以遍历集合也可以遍历数组
Collection<String> list = new ArrayList<>();
for(String ele : list) {
System.out.println(ele);
}
注意:不能通过这种方法修改其中的元素值,没有用
21.4.3 Lambda表达式
方法名称 |
作用 |
default void forEach(Consumer<? super T> action) |
结合lambda遍历集合 |
书写格式与化简
Collection<String> c1 = new ArrayList<>();
c1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//可化简为
c1.forEach(s -> System.out.println(s));
// 还可以化简为
c1.forEach(System.out::println);
综合案例题
我们之前已经做过番剧的存储与展示了,再用所学知识试一次
//要点1
Collection<Movie> arr = new ArrayList<>();
//泛型的类型是我们自己写的Movie类
//要点2:输出时需要在Moive类中重写toString方法,否则仅输出地址
总结:
List集合的遍历方式:
迭代器
增强for循环
Lambda表达式
for循环(因为List集合存在索引)
22 常见数据结构
数据结构定义:计算机底层存储、组织数据的方式。即数据相互之间是以什么方式排列在一起的。
存在意义:精心选择的数据结构可以带来更高的运行或者存储效率
包含:栈 队列 数组 链表 二叉树 二叉查找树 平衡二叉树 红黑树 等
22.1 栈
执行特点:后进先出,先进后出
可以想象为弹匣,最上面的子弹是最后进入的,却最早发射
数据进入栈模型的过程称为:压/进栈
数据离开栈模型的过程称为:弹/出栈
22.2 队列
执行特点:先进先出,后进后出
就想象普通排队
数据从后端进入队列模型的过程称为:入队列
数据从前端离开队列模型的过程称为:出队列
22.3 数组
查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。(元素在内存中是连续存储的)
删除效率低:要将原始数据删除,同时后面每个数据前移。
添加效率极低:添加位置后的每个数据后移,再添加元素。
小结:数组是一种查询快,增删慢的模型
22.4 链表
链表中的元素是在内存中不连续存储的,每个元素节点包含数据值和下一个元素的地址。
链表分类:单项列表,双向链表(双向链表包含前一个和下一个元素的地址)
链表查询慢。无论查询哪个数据都要从头开始找
链表增删相对快,只用修改“下一个元素的地址”即可
小结:相比数组而言,链表查询慢,增删快
22.5 二叉树
二叉树的一个节点包含父节点地址,自身值,左子节点,右子节点地址
二叉树的特点:
只能有一个根节点,每个节点最多支持2个直接子节点。
节点的度: 节点拥有的子树的个数,二叉树的度不大于2
叶子节点:度为0的节点,也称之为终端结点。
高度:叶子结点的高度为1,叶子结点的父节点高度为2,以此类推,根节点的高度最高。
层:根节点在第一层,以此类推
兄弟节点 :拥有共同父节点的节点互称为兄弟节点
22.5.1 二叉查找树
又称二叉排序树或者二叉搜索树
特点:
1,每一个节点上最多有两个子节点
2,左子树上所有节点的值都小于根节点的值
3,右子树上所有节点的值都大于根节点的值
目的:提高检索数据的性能。
二叉树查找树添节点
规则: 小的存左边 大的存右边 一样的不存
22.5.2 平衡二叉树
由于二叉树添加节点的规则,有可能导致左子树或右子树过于长
因此平衡二叉树是在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。
定义:任意节点的左右两个子树的高度差不超过1,任意节点的左右两个子树都是一颗平衡二叉树
当平衡二叉树在添加元素后不平衡时,基本策略是进行左旋,或者右旋保证平衡。
平衡二叉树-旋转的四种情况:左左 左右 右右 右左
22.5.3 红黑树
定义:红黑树是一种自平衡的二叉查找树,是计算机科学中用到的一种数据结构。
每一个节点可以是红或者黑;红黑树不是通过高度平衡的,它的平衡是通过“红黑规则”进行实现的。
其节点包含:父节点地址,自身值,左子节点右子节点地址,颜色
红黑规则:
每一个节点或是红色的,或者是黑色的,根节点必须是黑色
如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的;
如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点。
添加节点:
添加的节点的颜色,可以是红色的,也可以是黑色的。默认用红色效率高。
小结:
红黑树不是高度平衡的,它的平衡是通过"红黑规则"进行实现的
红黑树增删改查的性能都很好
总结:
队列:先进先出,后进后出。
栈:后进先出,先进后出。
数组:内存连续区域,查询快,增删慢。
链表:元素是游离的,查询慢,首尾操作极快。
二叉树:永远只有一个根节点, 每个结点不超过2个子节点的树。
查找二叉树:小的左边,大的右边,但是可能树很高,查询性能变差。
平衡查找二叉树:让树的高度差不大于1,增删改查都提高了。
红黑树(就是基于红黑规则实现了自平衡的排序二叉树)
23 List系列集合
回顾:
List系列集合特点
ArrayList、LinekdList :有序,可重复,有索引。
有序:存储和取出的元素顺序一致
有索引:可以通过索引操作元素
可重复:存储的元素可以重复
List集合特有方法
List集合因为支持索引,所以多了很多索引操作的独特api
方法名称 |
作用 |
void add(int index,E element) |
在此集合中的指定位置插入指定的元素 |
E remove(int index) |
删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) |
修改指定索引处的元素,返回被修改的元素 |
E get(int index) |
返回指定索引处的元素 |
23.1 ArrayList集合底层原理
ArrayList底层是基于数组实现的:根据索引定位元素快,增删需要做元素的移位操作。
第一次创建集合并添加第一个元素的时候,在底层创建一个默认长度为10的数组。
添加进去之后,size向后移动一个位置。
当10存满之后需要扩容,【当size等于当前数组长度时就会扩容至当前长度的1.5倍】
23.2 LinkedList
特点:
底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以多了很多首尾操作的特有API。
LinkedList集合的特有功能:
方法名称 |
作用 |
public void addFirst(E e) |
在该列表开头插入指定的元素 |
public void addLast(E e) |
将指定的元素追加到此列表的末尾 |
public E getFirst() |
返回此列表中的第一个元素 |
public E getLast() |
返回此列表中的最后一个元素 |
public E removeFirst() |
从此列表中删除并返回第一个元素 |
public E removeLast() |
从此列表中删除并返回最后一个元素 |
LinkedList可以完成队列结构和栈结构(双链表)
1.创建一个栈对象[想使用独有功能,就不能用多态啦
LinkedList<String> stack = new LinkedList<>();
// 压栈,入栈
stack.addFirst("第一颗子弹");
stack.addFirst("第2颗子弹");
stack.addFirst("第3颗子弹");
stack.addFirst("第4颗子弹");
stack.addFirst("第5颗子弹");
System.out.println(stack);
// 出栈,弹栈
System.out.println(stack.getFirst());
System.out.println(stack);
System.out.println(stack.removeFirst());//把第一个位置的元素拿到并移走
System.out.println(stack);
// 所以如果打三枪
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack);
// 队列
LinkedList<String> queue = new LinkedList<>();
// 入队
queue.addLast("1号");
queue.addLast("2号");
queue.addLast("3号");
queue.addLast("4号");
System.out.println(queue);
// 出队
System.out.println(queue.getFirst());
System.out.println(queue);
System.out.println(queue.removeFirst());//出来之后从队伍中删掉
System.out.println(queue);
// 但是使用addFirst之类的并不专业。
// 压栈:
stack.push("最后一颗子弹");
System.out.println(stack);
// 弹栈:
stack.pop();
System.out.println(stack);
// 入队
queue.offerLast("最后号");
System.out.println(queue);
总结与补充
从集合中的一批元素中找出某些数据并删除时可能存在异常
也就是并发修改异常
迭代器遍历集合且直接用集合删除元素的时候可能出现。
//这个集合中存放着一些数据
ArrayList<String> list = new ArrayList<>();
//假设我们要删除这个集合中的所有“早安”
Iterator<String> it = list.iterator();//创建一个迭代器
while (it.hasNext()){
String ele = it.next();
if("早安".equals(ele)){
// 删除早安
// list.remove(ele); // 集合删除会出毛病
it.remove(); // 删除迭代器所在位置的元素值,不会后移,所以使用这个更好
}
}
增强for循环遍历集合且直接用集合删除元素的时候可能出现。
//这个集合中存放着一些数据
ArrayList<String> list = new ArrayList<>();
//假设我们要删除这个集合中的所有“早安”
for (String s : list) {
if("Java".equals(s)){
list.remove(s);
}
}
这种无法解决的,foreach不能边遍历边删除,会出bug,所有不要用增强for
另外我们之前使用的for循环也要记得倒着遍历删除
因此:
迭代器遍历集合但是用迭代器自己的删除方法操作
for循环倒着遍历删除
这两种方式都是可以的
24 泛型
定义:可以在编译阶段约束操作的数据类型,并进行检查
是JDK5中引入的特性
格式:<数据类型>;
泛型只能支持引用数据类型。
集合体系的全部接口和实现类都是支持泛型的使用的。
优点:
统一数据类型。
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常。
定义泛型:
在 类 后 定义就是 泛型类
在 方法申明 上 定义就是 泛型方法
在 接口 后 定义就是 泛型接口
24.1 泛型类
定义:定义类时同时定义了泛型的类就是泛型类。
书写格式:
public class MyArrayList<T> { }
此处泛型变量T可以随便写为任意标识,常见的如E、T、K、V等。
案例:
public class MyArrayList<T> {
}
作用:编译阶段可以指定数据类型,类似于集合的作用。
原理:把出现泛型变量的地方全部替换成传输的真实数据类型。
小案例题:自定义一个集合,加入“泛型”这个要素
//想写一个泛型类的话,这样写就足够了
public class MyArrayList<E> {
//但是最好还是有一些功能比较好
private ArrayList list = new ArrayList();//像饿汉单例
//添加数据
public void add(E e) {
list.add(e);
}
//删除数据
public void remove(E e) {
list.remove(e);
}
//不要忘记重写toString
}
24.2 泛型方法
定义:定义方法时同时定义了泛型的方法就是泛型方法
书写格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
作用:方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
案例:
public <T> void show(T t) {
}
原理:把出现泛型变量的地方全部替换成传输的真实数据类型。
小案例题:
给出任何一个类型的数组,都能返回它的内容
接收类型就是泛型
之后就正常写了
public static <T> void printArray(T[] arr){
// 要考虑数组为空的情况
if(arr != null){
StringBuilder sb = new StringBuilder("[");
for (int i =0;i<arr.length;i++ ) {
sb.append(arr[i]).append(i == arr.length - 1?"":",");
}
sb.append("]");
System.out.println(sb);
}else {
System.out.println(arr);
}
}
// 假设这里有一个add方法,我们可以通过泛型让他返回的值也是T,不需要强转
public static <T> T[] add(T[] arr){
return arr;
}
}
24.3 泛型接口
定义:使用了泛型定义的接口就是泛型接口。
书写格式:修饰符 interface 接口名称<泛型变量>{}
作用:泛型接口可以约束实现类,可以让实现类选择当前功能需要操作的数据类型
原理:实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。
案例
public interface Data<E>{
}
小案例题:
设计一个泛型的人类接口,让老师类和学生类都能继承
public interface Data<E> {
// 使用泛型接口,不管是老师类还是学生类都可以使用增删改查
void add(E e);
void delete(int id);
void update(E e);
E queryById(int id);//这里的E就是返回值
}
24.4 泛型通配符
定义:“?”就是通配符
作用:“使用泛型”的时候代表一切类型。
而E T K V 是在定义泛型的时候使用的。
我们需要一个小案例题来帮助理解
小案例题:
模拟赛车,让不同牌子的车都可以参加
解题思路:
不同牌子的车就意味着不同“类”
我们需要一个“车”类作为父类
再写一些子类去继承它,例如奔驰宝马什么的,随你喜欢
有了具体的“奔驰”类,我们就创建一些具体的车对象出来
最后有了车,就该写“比赛”方法了
这时
//所有车比赛,使用?代表所有类型
public static void go(ArrayList<?> cars){
}
我们可以注意到,这里的泛型就用的是通配符,代表一个车的类型集合仅存放这一个类型
泛型的上下限:
? extends XXX(类名): ?必须是XXX或者其子类 泛型上限
? super XXX(类名) : ?必须是XXX或者其父类 泛型下限
这有什么作用呢?
在上面的案例中,即便不是车,也能够参与比赛,这样是不行的,因此
// 所以应该这样
public static void go(ArrayList< ? extends Car> cars){
}
在尖括号中的?extends Car 就是泛型上限,代表传入的类必须继承自车类才行。
这样就能够防止不是车的别的类也加入比赛。
25 Set系列集合
回顾一下set集合的特点:
1.无序:存取顺序不一致
这里的无序只无序一次,不是每一次都是打乱的
2.不重复:可以去除重复
3.无索引:没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素。
Set集合实现类特点:
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
这里的有序指:保证存储和取出的元素顺序一致
TreeSet:可排序、不重复、无索引。
可排序指天然对存储的数据升序排序
Set集合的功能上基本上与Collection的API一致
25.1 HashSet底层原理
HashSet集合底层采取哈希表存储的数据。
其中:
哈希表的组成:
JDK8之前的,底层使用数组+链表组成
JDK8开始后,底层采用数组+链表+红黑树组成。
哈希表是一种对于增删改查数据性能都较好的结构。
我们先了解一下“哈希值”
定义:是JDK根据对象的地址,按照某种规则算出来的int类型的数值。
可以将“这个数值”理解为地址的另一种表示方法
Object类的API:
public int hashCode():返回对象的哈希值
对象的哈希值特点:
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。
哈希值的特点在下方会详细解释。
我们通过案例来了解一下哈希值的模样
String name = "桂小太郎";
System.out.println(name.hashCode());//818514353
通过这个案例,我们发现name对象的哈希值为818514353,当然哈希值的长度有变化不过都是int类型
25.1.1 HashSet1.7版本原理解析
底层使用数组+链表组成
1.在创建对象的时候
Set<String> sets = new HashSet<>();
创建一个默认长度16的数组,数组名table ;
2.根据元素的哈希值跟数组的长度求余计算出应存入的位置(哈希算法)
假设元素的哈希值为16,而数组的长度也为16,那么余数为0,则应存入索引为0的位置
也因此这个值一定会在0到15之间
3.判断当前位置是否为null,如果是null直接存入。
如果位置不为null,表示有元素,则调用equals方法比较
如果一样,则不存,如果不一样,则存入数组
JDK 7新元素占老元素位置,指向老元素【也就是说新元素存入数组,而以链表的形式与老元素连接】
JDK 8中新元素挂在老元素下面【也就是说新元素直接以链表的形式与老元素连接】
当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
因此:哈希表是一种对于增删改查数据性能都较好的结构。
25.1.2 HashSet1.8版本原理解析
底层采用数组+链表+红黑树组成。
由于:挂在元素下面的数据过多时,查询性能降低,从JDK8开始后,当链表长度超过8的时候,自动转换为红黑树。
其余与1.7版本相同
哈希表对于红黑树的引入进一步提高了操作数据的性能。
25.1.3 HashSet去重复原理解析
我们已经知道了原理,这里更多的是对代码的展示
如果希望Set集合认为2个内容一样的对象是重复的, 必须重写对象的hashCode()和equals()方法
我们在之前的学习中已经了解了equals()
我们在一个小案例题中具体了解
小案例题:
创建一个存储角色对象的集合,存储多个角色对象,使用程序实现在控制台遍历该集合,要求:学生对象的成员变量值相同,我们就认为是同一个对象
解题思路:定义角色类,创建HashSet集合对象, 创建角色对象,之后把角色添加到集合里,在学生类中重写两个方法,hashCode()和equals(),可以自动生成,使用增强for遍历集合【直接输出也行】
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
hashCode的源代码比较复杂,但我们可以理解,因为入参name, age, sex是一样的,因此结果是一样的,因此哈希值是一样的。
有了一样的哈希值,再通过equals判断内容是否一样,就可以达到去重的目的
25.2 LinkedHashSet底层原理
LinkedHashSet的特点是:有序、不重复、无索引。
有序是如何实现的呢?
原理:底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
比如第一个数据会记录第二个加入进来的数据的地址。
25.3 TreeSet底层原理
特点:不重复、无索引、可排序
TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好。
注意:TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
排序情况:
对于数值类型:Integer , Double,官方默认按照大小进行升序排序
对于字符串类型:默认按照首字符的编号升序排序。【ACS2码的编号,所以中文一般都更靠后】
对于自定义类型:如Student对象,TreeSet无法直接排序,需要制定排序规则。
自定义排序规则方式:
方式一 让自定义的类(如学生类)实现Comparable接口重写里面的compareTo方法来定制比较规则。
方式二 TreeSet集合有参数构造器,可以设置Comparator接口对应的比较器对象,来定制比较规则。
其返回值:
如果认为第一个元素大于第二个元素返回正整数即可。
如果认为第一个元素小于第二个元素返回负整数即可。
如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序。
这两种方式我们之前已经使用过了
小案例题:
假设你开了一家苹果店,卖不同种类的苹果,在展示的时候按照某种排序顺序展示你的苹果们
解题思路:定义苹果类,设置相关属性,例如商品名,重量,颜色,价格等属性
创建多个苹果对象,存入TreeSet集合,并设定排序规则,最后输出
/**
方式一:类自定义比较规则。写在苹果类中
o1.compareTo(o2) 使用的时候是这样的
* @param o
* @return
*/
@Override
public int compareTo(Apple o) {
// 按照重量进行比较的
return this.weight - o.weight ; // 去除重量重复的元素
// return this.weight - o.weight >= 0 ? 1 : -1; // 保留重量重复的元素(肯定有重量相同的苹果的)
}
方式二:集合自带比较器对象进行规则定制
Set<Apple> apples = new TreeSet<>(new Comparator<Apple>() {//匿名内部类也可以快捷生成
@Override
public int compare(Apple o1, Apple o2) {
// return o1.getWeight() - o2.getWeight(); // 升序
// return o2.getWeight() - o1.getWeight(); // 降序
// 注意:浮点型建议直接使用Double.compare进行比较
// return Double.compare(o1.getPrice() , o2.getPrice()); // 升序
return Double.compare(o2.getPrice() , o1.getPrice()); // 降序
}
});
方式二可化简为
Set<Apple> apples = new TreeSet<>(( o1, o2) ->Double.compare(o2.getPrice() , o1.getPrice()));
25.4 Collection体系的特点、使用场景总结
如果希望元素可以重复,又有索引,索引查询要快
用ArrayList集合,基于数组的。(用的最多)
如果希望元素可以重复,又有索引,增删首尾操作快
用LinkedList集合,基于链表的。
如果希望增删改查都快,但是元素不重复、无序、无索引。
用HashSet集合,基于哈希表的。
如果希望增删改查都快,但是元素不重复、有序、无索引。
用LinkedHashSet集合,基于哈希表和双链表。
如果要对对象进行排序。
用TreeSet集合,基于红黑树。后续也可以用List集合实现排序。
25.5 可变参数
作用:可变参数用在形参中可以接收多个数据。
也就是说不规定只能接受多少个数据,而是任意
书写格式:数据类型...参数名称
优点:接收参数非常灵活,方便。可以不接收参数,可以接收1个或者多个参数,也可以接收一个数组
可变参数在方法内部本质上就是一个数组。
注意事项:
1.一个形参列表中可变参数只能有一个
2.可变参数必须放在形参列表的最后面
小案例题:
定义一个方法求和,该方法可以灵活的完成如下需求: 计算1个数据的和。 计算2个数据的和。 计算3个数据的和。 计算n个数据的和,甚至可以支持不接收参数进行调用。
// 写一个求和的方法
public static void sum(int...nums){
// 注意,可变参数在方法内部其实就是一个数组
System.out.println("元素个数" + nums.length);
System.out.println("元素内容" + Arrays.toString(nums));
public static void main(String[] args) {
// 1.不传参数
sum();
// 2.传一个参数
sum(10);
// 3.传输多个参数
sum(10,30,38);
// 4.传输一个数组
int[] arr = {10,30,30};
sum(arr);
}
25.6 集合工具类Collections
java.utils.Collections:是集合工具类。Collections并不属于集合
作用:用来操作集合的工具类。
Collections常用的API
方法名称 |
作用 |
public static <T> boolean addAll(Collection<? super T> c, T... elements) |
给集合对象批量添加元素 |
public static void shuffle(List<?> list) |
打乱List集合元素的顺序 |
shuffle:每次随机。原理就是随机数随机索引之后交换位置。具体使用例如洗牌
Collections排序相关API
使用范围:只能对于List集合的排序。
排序方式1:
public static <T> void sort(List<T> list)
将集合中元素按照默认规则排序
注意:本方式不可以直接对自定义类型的List集合排序,除非自定义类型实现了比较规则Comparable接口。
排序方式2:
public static <T> void sort(List<T> list,Comparator<? super T> c)
将集合中元素按照指定规则排序
综合案例题:
模拟制作斗地主游戏,仅仅制作到发牌完毕的阶段即可
解题思路:
我们曾经模拟过斗地主,仍旧应定义一个静态集合存储原始的54张扑克牌
之后使用静态代码块创建扑克牌:即通过循环嵌套制作出“牌”,并通过index变量存放牌的大小以便比较
之后在主方法中进行发牌:首先应该洗牌,使用api打乱顺序,并通过循环给三位玩家发牌。
使用api截取最后三张牌作为保留
最好单独写一个排序方法,帮助玩家整理手牌。
26 Map集合
定义:Map集合是一种双列集合,每个元素包含两个数据。也被称为“键值对集合”
书写格式:
Collection集合的格式: [元素1,元素2,元素3..]
Map集合的完整格式:{key1=value1 , key2=value2 , key3=value3 , ...}
其中:Map集合的键:无序、不重复的
Map集合的值 值不做要求 可以重复
Map集合体系特点:
Map集合的特点都是由键决定的。
Map集合的键是无序,不重复的,无索引的,值不做要求(可以重复)。
Map集合后面重复的键对应的值会覆盖前面重复键的值。
Map集合的键值对都可以为null。
Map集合使用场景:例如购物车,商品对象作为键,购买数量作为值。
26.1 Map集合体系及其特点
Map (接口) |
||
HashMap (实现类) |
HashTable (实现类) |
\(接口) |
LinkedHashMap (实现类) |