一、集合概述
| 概述 | Java中存储对象数据的一种容器; |
|---|---|
| 特点 | 集合大小不固定,类型不固定,启动后动态变化; 适合元素增删操作; |
| 注意 | 集合只能存储引用类型数据,基本数据类型选用包装类; 集合存储元素对象地址; |
二、Collection集合体系特点
1.集合有两种:
- 单列集合(祖宗接口)Collection
- 接口(接口层)
- List(元素有序、可重复、有索引)
- (实现类层)
- ArrayList
- LinkedList
- Set(元素无序、不重复、无索引)
- HashSet
- LinkedHashSet(改良,有序)
- TreeSet(按照大小默认升序排序,不重复,无索引)
- HashSet
- 双列集合Map
2集合对于泛型的支持
- 集合在编译阶段约束集合只能操作某种数据类型;
- 集合只支持引用数据类型,存储的都是对象;
2.1集合存储基本类型方式
//JDK1.7后,后面<>内容可以省略
Collection<Integer> list = new ArrayList<>();
Collection<Integer> list = new ArrayList<Integer>();
Collection<Double> list = new ArrayList<>();
3. Collection集合API
/**
* Collection类的API
*/
public class CollectionAPIDemo01 {
public static void main(String[] args) {
//1.添加元素
Collection list = new ArrayList();
list.add("java");
list.add("c++");
System.out.println(list.add("java"));//true
list.add(520);
System.out.println(list);//[java, c++, java, 520]
//2.清空集合
list.clear();
System.out.println(list);//[]
//3.判断集合是否为空,空返回true
boolean empty = list.isEmpty();
//4.获取集合大小
int size = list.size();
//5.判断集合是否包含某个元素
boolean java = list.contains("java");
//6.删除某个元素:有多个,删除第一个
boolean java1 = list.remove("java");
//7.集合转换成数组:使用Object接收一切类型
Object[] arrs = list.toArray();
System.out.println(Arrays.toString(arrs));
//元素合并
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
Collection c2 = new ArrayList();
c2.add("c");
c1.addAll(c2);
System.out.println(c1);//[a, b, c]
System.out.println(c2);//[c]
}
}
4. Collection集合遍历方式
4.1 迭代器Iterator
迭代器Iterator,是集合专用遍历方式;
/**
* 迭代器Iterator
*/
public class CollectionDemo01 {
public static void main(String[] args) {
Collection<String> list = new ArrayList();
list.add("java");
list.add("java2");
list.add("java3");
//iterator集合器对象,默认指向索引0,集合第一个元素
Iterator<String> iterator = list.iterator();
//iterator.hasNext(),判断当前位存在元素吗
while (iterator.hasNext()){
//取出元素,iterator后移一位
String ele = iterator.next();
System.out.println(ele);
}
//NoSuchElementException元素越界异常
String ele = iterator.next();
}
}
4.2 foreach/增强for循环
- 既可以遍历集合,也可以遍历数组
- 不能用for,Collection接口不支持索引
- JDK5后出现,内部原理是Iterator迭代器,一种简化写法;
- 实现Iterator接口才可以使用迭代器和增强for,Collection接口实现了
/**
* 增强for:
* 数组、集合可用
*/
public class CollectionDemo02 {
public static void main(String[] args) {
Collection<String> list = new ArrayList();
list.add("java");
list.add("java2");
list.add("java3");
for (String ele : list) {
System.out.println(ele);
}
//数组可以用
double[] arrs = {102.5,363.9,888.8};
for (double arr : arrs){
System.out.println(arr);
}
/**
* 102.5
* 363.9
* 888.8
*/
}
}
4.3 lambda 表达式
/**
* 增强forEach:
* lambda 表达式
*/
public class CollectionDemo03 {
public static void main(String[] args) {
Collection<String> list = new ArrayList();
list.add("java");
list.add("java2");
list.add("java3");
//forEach
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//lambda简化
list.forEach( s -> System.out.println(s));
//lambda简化进一步
list.forEach( System.out::println);
}
}
三、常见数据结构
1.数据结构概述
- 数据结构是计算机底层存储、组织数据的方式;数据相互间以什么方式排列;
- 通常,选择好的数据结构可以更高的运行存储效率;
2.常见数据结构
2.1栈
2.1.1栈数据结构的执行特点
后进先出,先进后出,单向开口,类似弹夹,称为压栈;
2.2队列
2.2.1队列的执行特点
- 先进先出,双向开口,排队
- 数据后端进,入队列
- 数据前端出,出队列;
2.3 数组
- 查询速度快:
- 查询数据通过地址值和索引定位,查询任意数据耗时相同
- 元素内存中连续存储;
- 删除效率低;
- 删除数据,后面所有要前移
- 添加效率低
- 牵连大量元素;
2.4 链表
2.4.1链表特点
- 链表中元素在内存中不连续存储,每个元素包含数据值和下一个元素地址;
- 查询慢,
- 查每个元素都从头开始
- 增删快:
- AC间添加B。
- B对应的下一个地址指向C
- A对应的下一个地址指向B
2.4.2 链表种类
- 单向链表:
- 双向链表:
- 每个元素有前一个和后一个地址,数据值
- 增加首尾元素特别快
2.5 二叉树
2.5.1 二叉树特点
| 只能有一个根节点 | 每个节点最多支持2个直接子节点; |
|---|---|
| 节点的度 | 节点拥有子节点的个数,不大于2 叶子节点度为0,终端节点; |
| 高度 | 叶子节点高度为1,向上推,根节点最高 |
| 层 | 根节点第一层,向下推 |
| 兄弟节点 | 拥有共同父节点的节点,互称兄弟节点 |
2.6 二叉查找数
| 二叉排序树,二叉搜索树 | |
|---|---|
| 特点: | 每个节点,最多两个子节点 左子树,所有节点值都小于根节点; 右子树,所有节点值,都大于根节点; |
| 目的 | 提高检索性能 |
| 缺陷 | 可能数据集中在一条子树上,成为瘸子; 查询性能与单链表一样,速度变慢 |
2.7 平衡二叉树
2.7.1特点
满足二叉查找树的规则下,让数尽可能矮小,提高查询性能;
2.7.2 平衡二叉树要求
- 任意节点的左右两个子树的高度差不超过1;
- 第一个,10节点,左子树高度0,右子树高度3,超过1,不是;
- 任意节点左右两个子树都是一颗平衡二叉树;
- 添加元素后,可能导致不平衡
- 策略:进行左旋或右旋保持平衡
- 左左:左子树高,再加左子树,右旋
- 左右:左子树高,再加左子树的右子树,右旋不行,先以添加父节点为基点,左旋,再右旋;
- 右右:同上
- 右左:
2.7.3平衡二叉树案例
2.8 红黑树
2.8.1 红黑树概述
- 红黑树是一种自平衡的二叉查找树;
- 1972,称为平衡二叉B树;1978称为红黑树;
- 每个节点可以是红或者黑;不是通过高度平衡,通过红黑规则实现;
2.8.2 红黑规则
- 每个节点是红色或黑色,根节点必须是黑色;
- 如果一个节点没有子节点或者父节点,该节点相应的指针属性值为Nil,Nil视为叶节点,叶节点是黑色的;
- 某一节点是红色,它的子节点必须是黑色(不能出现两个红节点相连)
- 每一个节点,从该节点到所有后代叶节点的简单路径上,均包含相同数目黑色节点;
2.8.3 添加节点
- 添加节点颜色,可以是红色,也可以是黑色;
- 默认用红色效率高;
- 还需要看
四、List系列集合
1.List集合特点和特有API
1.1特点
- 有序:存储、取出元素顺序一致;
- 有索引:通过索引操作元素
- 可重复:存储元素
1.2 特有API
/**
* List独有API
*/
public class ListDemo01 {
public static void main(String[] args) {
//List 有序,可重复,有索引
List<String> list = new ArrayList();
list.add("java");
list.add("java2");
list.add("java3");
//2.在某个索引插入元素
list.add(1,"java22");
//[java, java22, java2, java3]
System.out.println(list);
//3.根据索引删除元素,返回删除的元素
//java22
System.out.println(list.remove(1));
System.out.println(list);
//4.根据索引,获取元素
System.out.println(list.get(1));//java2
//5.修改指定索引位置元素,返回修改前数据
System.out.println(list.set(1, "java222"));//java2
System.out.println(list);//[java, java222, java3]
}
}
2.List集合遍历方式
/**
* List循环遍历
*/
public class ListDemo02 {
public static void main(String[] args) {
//List 有序,可重复,有索引
List<String> list = new ArrayList();
list.add("java");
list.add("java2");
list.add("java3");
//1.for
for (int i = 0; i < list.size(); i++) {
String ele = list.get(i);
System.out.println(ele);
}
System.out.println("===========================");
//2.迭代器Iterator
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String ele = iterator.next();
System.out.println(ele);
}
//3.forEach
for (String l : list){
System.out.println(l);
}
//4.Lambda表达式
list.forEach(s -> System.out.println(s));
//5.Lambda表达式 再简化
list.forEach(System.out::println);
}
}
3.ArrayList集合底层原理
- 底层基于数组实现;根据索引定位元素,增删需要元素移位操作;
- 第一次创建集合并添加第一个元素时,底层创建一个默认长度为10的数组;
- 元素超过容量
- 扩容,增加为原来的1.5倍
- 插入新元素到中间位置
- 倒序遍历,将索引后移,到指定位置,插入元素
4. LinkedList集合
4.1 LinkedList特点
底层数据结构是双链表,查询慢,首尾操作快,有很多首尾操作API
4.2 LinkedList集合特有功能
/**
* LinkedList API
*/
public class ListDemo03 {
public static void main(String[] args) {
//LinkedList可以完成队列结构,栈结构(双链表)
LinkedList<String> stack = new LinkedList();
//压栈、入栈
stack.addFirst("第1颗子弹");
stack.addFirst("第2颗子弹");
stack.addFirst("第3颗子弹");
stack.addFirst("第4颗子弹");
System.out.println(stack.getFirst());//第4颗子弹
System.out.println(stack.getLast());//第1颗子弹
//[第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
System.out.println(stack);
//出栈、弹栈
System.out.println(stack.removeFirst());//第4颗子弹
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());//第1颗子弹
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);//[1号, 2号, 3号, 4号]
//出队
System.out.println(queue.removeFirst());//1号
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());//4号
System.out.println(queue);
/**
* 压栈
* public void push(E e) {
* addFirst(e);
* }
*/
stack.push("压栈");
/**
* 弹栈
* public E pop() {
* return removeFirst();
* }
*/
stack.pop();
/**
* 入队
* public boolean offerLast(E e) {
* addLast(e);
* return true;
* }
*/
queue.offerLast("入队");
}
}
五、集合并发修改异常问题
集合中找出某元素并删除的时候,出现并发修改异常;
5.1哪些遍历存在问题?
-
增强for遍历,直接用集合删除元素可能出现;
- 不能解决该问题,不能用
-
forEach不能解决5.1.1 迭代器遍历,直接用集合删除元素可能出现;
/** * 并发修改异常 */ public class Test { public static void main(String[] args) { List<String> list = new ArrayList(); list.add("java"); list.add("java"); list.add("java2"); list.add("java3"); list.add("java3"); //Iterator Iterator<String> iterator = list.iterator(); while (iterator.hasNext()){ String ele = iterator.next(); if ("java".equals(ele)){ //ConcurrentModificationException 并发修改异常 //list.remove("java"); 后移会漏删, iterator.remove();//删除当前位置元素,,内部删除后返回当前位置 } } System.out.println(list); } }5.1.2 for循环,不会报错,但数据异常,需要使用解决方案
//for //不报错,数据会漏删 /* for (int i = 0; i < list.size(); i++) { String ele = list.get(i); if ("java".equals(ele)){ list.remove("java"); } }*/ //解决方案,倒序删除 for (int i = list.size()-1; i >= 0; i--) { String ele = list.get(i); if ("java".equals(ele)){ list.remove("java"); } } System.out.println(list); } //解决方案2,i-- for (int i = 0; i < list.size(); i++) { String ele = list.get(i); if ("java".equals(ele)){ list.remove("java"); i--; } } System.out.println(list); }
六、泛型深入
1.泛型概述
- 泛型:JDK5中引入特性,可以在编译阶段约束操作的数据类型,并进行检查;
- 泛型格式:<数据类型>
- 注意:只能使用引用数据类型,不能用基本数据类型
- 集合体系的全部接口和实现类都是支持泛型的使用
2.泛型好处
- 统一数据类型;
- 运行期问题提前到编译期,避免了强制类型转换可能出现的异常,编译阶段类型就能确定下来
3.自定义泛型类
3.1概述
-
定义类,同时定义了泛型,就是泛型类;
-
格式:修饰符 class 类名<泛型变量>{}
-
public class MyArrayList<T>{}
-
-
泛型变量T可以写为任意表示,常用E T K V
-
作用:编译阶段指定数据类型
3.2泛型类:原理
把出现泛型变量的地方,全部替换成传输的真实数据类型;
3.3 案例:模拟ArrayList
**装饰模式:**一个对象里,包另一个对象
/**
* 模拟ArrayList
* 实现 泛型,简单的添加删除
*/
public class MyArrayList<E> {
private ArrayList arrayList = new ArrayList();
public void add(E e){
arrayList.add(e);
}
public boolean remove(E e){
return arrayList.remove(e);
}
@Override
public String toString() {
return arrayList.toString();
}
}
/**
* 测试 MyArrayList
*/
public class Test {
public static void main(String[] args) {
MyArrayList<String> list = new MyArrayList<>();
list.add("java");
list.add("java2");
list.add("java3");
System.out.println(list.toString());//[java, java2, java3]
list.remove("java");
System.out.println(list.toString());//[java2, java3]
}
}
4.自定义泛型方法
4.1 泛型方法概述
- 定义方法,同时定义了泛型,就是泛型方法;
- 格式:修饰符 <泛型变量> 方法返回值 方法名称(形参列表){}
- 作用:方法中可以使用泛型接收一切实际类型参数,方法更具备通用性
4.2 演示
/**
* 泛型方法
* 作用:方法中可以使用泛型接收一切实际类型参数,方法更具备通用性
*/
public class GenericMethodDemo {
public static void main(String[] args) {
String[] a = {"saf","fasdf","fw","gtrg","hh"};
printArray(a);//[saf,fasdf,fw,gtrg,hh]
Integer[] b = {25,66,999,545,25};//[25,66,999,545,25]
printArray(b);
Long[] c= {};//[]
printArray(c);
String[] aa = getArr(a);
Integer[] bb = getArr(b);
}
//泛型调用,返回不需要强转
public static<T> T[] getArr(T[] arr){
return arr;
}
//泛型,输出数组
public static<T> void printArray(T[] arr){
if (arr != null) {
StringBuilder stringBuilder = new StringBuilder("[");
for (int i = 0; i < arr.length; i++) {
stringBuilder.append(arr[i]).append(i == arr.length-1 ? "":",");
}
stringBuilder.append("]");
System.out.println(stringBuilder.toString());
}else {
System.out.println(arr);
}
}
}
5.泛型接口
接口格式:修饰符 interface 接口名称<泛型变量>
作用:可以让实现类选择当前功能需要操作的数据类型
原理:实现类指定自己操作的数据类型,重写方法针对该类型操作
/**
* 泛型接口
* @param <E>
*/
public interface Data<E> {
void add(E e);
void delete(int id);
void update(E e);
E queryById(int id);
}
//Data<Teacher> 类型Teacher必须自行先定义
public class TeacherData implements Data<Teacher> {
@Override
public void add(Teacher teacher) {
}
@Override
public void delete(int id) {
}
@Override
public void update(Teacher teacher) {
}
@Override
public Teacher queryById(int id) {
return null;
}
}
6.泛型通配符、上下限
1.通配符
?可以在使用泛型时代表一切类型;- E T K V 定义泛型时使用
2.上下限
?extends Car:? 必须是Car或者其子类 ,泛型上线;?super Car:? 必须是Car或者其父类 ,泛型下线;
七、Set集合体系
1.Set系列集合特点
| 无序 | 存取顺序不一致(第一次无序,后面一样) |
|---|---|
| 不重复 | 可以用来去重 |
| 无索引 | 不能使用普通for循环,不能通过索引取元素 |
2.Set集合实现类特点
| HashSet | 无序、不重复、无索引 |
|---|---|
| LinkedHashSet | 有序、不重复、无索引 |
| TreeSet | 排序、不重复、无索引 |
/**
* Set系列集合特点
*/
public class SetDemo {
public static void main(String[] args) {
//无序,不重复 [java, mysql, 123456qq, 459qq]
//Set<String> sets = new HashSet<>();
//有序,不重复 无索引 [java, 123456qq, 459qq, mysql]
Set<String> sets = new LinkedHashSet<>();
sets.add("java");
sets.add("java");
sets.add("123456qq");
sets.add("459qq");
sets.add("mysql");
sets.add("mysql");
System.out.println(sets);
}
}
3.HashSet底层原理
- HashSet集合底层采取哈希表存储的数据;
- 哈希表是一种对于增删改查数据性能都较好的结构;
3.1哈希表组成
- JDK8之前,底层使用数组+链表组成;
- JDK8之后,底层使用数组+链表+红黑树组成;
3.1.1 哈希值
- 是JDK根据对象的地址,按照某种规则算出来的int类型数值;
3.1.2 Object类的API
public int hashCode();返回对象哈希值;
3.1.3 对象哈希值特点
- 同一对象多次调用hashCode()方法返回哈希值相同;
- 默认,不同对象,哈希值不同;
- 哈希值相同,对象不一定相同(可能不同地址计算出同一哈希值)
- 哈希值不同,一定不是同一对象;
3.2 JDK7 原理:数组+链表+(哈希算法)
- 创建一个默认长度16的数组,数组名table;
- 根据元素的哈希值跟数组长度求余(0-15)算出应存入位置(哈希算法);
- 存放位置不定,无序原因
- 判断当前位置是否null,是,直接存
- 如果不为null,有元素,调用equals方法比较值
- 一样,值重复不存;不一样,存入数组
- JDK7 ,新元素占老元素位置,用链表,指向老元素(放后面)
- JDK8,新元素挂在老元素下面;
3.3 JDK8 原理:哈希表(数组、链表、红黑树结合体)
- 当挂在元素下面的数据过多时,查询性能降低,JDK8之后,链表长度超过8时,自动转为红黑树
- 红黑树比较大小,根据哈希值比较,左右排
3.4 HashSet去重复原理解析
- 使用哈希表,下方;
- 结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法;
- 原因:通过对象地址计算hashCode();需要改为值计算;
4.哈希表的详细流程
- 创建一个默认长度16,默认加载因子0.75的数组,数组名table;
- 根据元素的哈希值跟数组长度求余(0-15)算出应存入位置(哈希算法);
- 判断当前位置是否null,
- 是,直接存;
- 如果不为null,有元素,调用equals方法比较值(值包含当前位置值和其下链表挂着的值);
- 一样,值重复不存;
- 不一样,存入数组,链表挂在当前值下
- 当数组存满到16*0.75=12时,自动扩容,每次扩容为原先的两倍
八、LinkedHashSet集合特点
- 有序、不重复、无索引;
- 有序:保证存储和取出的元素顺序一致;
- 原理:底层数据结构依然是哈希表,每个元素额外多了一个双链表的机制记录存储的顺序;
- 链表顺序按照元素存入顺序,交付前后地址,不是数组顺序
九、TreeSet集合
1.TreeSet集合概述和特点
- 不重复、无索引、可排序;
- 可排序:按照元素的大小默认升序(由小到大)排序;
- TreeSet集合底层是基于红黑树的结构实现排序,增删改性能都较好
- 注意:TreeSet集合一定要排序,可以将元素指定规则排序;
2.TreeSet集合默认的规则
- 数值类型:Integer、Double,默认按照大小进行升序排序;
- 字符串类型:默认按照首字符编号升序排序;
- 自定义类型:Student对象,无法直接排序;
- 结论:TreeSet存储自定义类型,需要先制定排序规则
3.自定义规则两种方式
- 类实现Comparable接口;
- 集合自定义Comparable比较器对象,重写比较规则;
十、总结
| ArrayList集合,基于数组(用的最多) | 元素可重复,又有索引,索引查询要快 |
|---|---|
| LinkedList集合,基于链表 | 元素可重复,又有索引,增删首尾要快 |
| HashSet集合,基于哈希表; | 增删改查都快,元素不重复,无序、无索引 |
| LinkedHashSet集合,基于哈希表+双链表; | 增删改查都快,元素不重复,有序、无索引 |
| TreeSet集合,基于红黑树; 后续可用List集合实现排序; | 对对象进行排序 |
十一、可变参数
1.可变参数概述
- 在形参中可以接收多个数据;
- 格式:数据类型...参数名称
2.可变参数作用
- 传输参数灵活,方便;可以不传输,也可以传输1个或多个,也可以传输数组
- 注意:可变参数在方法内部就是一个数组:nums
3.可变参数注意事项
- 一个形参列表中,可变参数只能有一个;
- 可变参数必须放在形参列表最后面;
/**
* 可变参数
*/
public class MethodDemo {
public static void main(String[] args) {
sum();//不传参
sum(10);//传参一个
sum(10,20);//传参多个
sum(new int[]{10,20,30,40,50});//传数组
}
public static void sum(int...nums){
//注意:可变参数在方法内部就是一个数组:nums
System.out.println(nums.length);
System.out.println(Arrays.toString(nums));
}
}
十二、Collections集合工具类
/**
* 添加多个public static <T> boolean addAll(Collection<? super T> c, T... elements)
* 打乱list顺序;public static void shuffle(List<?> list)
* 默认规则排序:public static <T extends Comparable<? super T>> void sort(List<T> list)
*/
public class CollectionsDemo01 {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("老大哥");
names.add("帝骑哥");
names.add("大聪明");
names.add("吃瘪龙");
//以下两种添加必须分开
Collections.addAll(names,"网瘾医生","老司机","鬼仔");
Collections.addAll(names,new String[]{"一人一半","投币"});
System.out.println(names);
//2.打乱list顺序;可以适用一切类型
//洗牌
//public static void shuffle(List<?> list)
Collections.shuffle(names);
System.out.println(names);
//3.默认规则排序
//Shift+F6 修改光标下的所有参数
List<Integer> lists = new ArrayList<>();
Collections.addAll(lists,15,678,99,42,61);
Collections.sort(lists);
System.out.println(lists);//[15, 42, 61, 99, 678]
}
}
/**
* 自定义规则排序,List
* public static <T> void sort(List<T> list, Comparator<? super T> c)
*/
public class CollectionsDemo02 {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("01", "飞电或人", 24,3546.9));
students.add(new Student("02", "高桥", 22,478.0));
students.add(new Student("03", "伊兹", 18,4565.9));
students.add(new Student("04", "鹤岛", 21,999.9));
Collections.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return Double.compare(o1.getPrice(), o2.getPrice());
}
});
//优化
Collections.sort(students, (o1,o2) -> Double.compare(o1.getPrice(), o2.getPrice()));
System.out.println(students);
}
}
十三、案例:斗地主
public class Card {
private String size;
private String color;
private int sum;//牌真正的比较点数
public Card() {
}
public Card(String size, String color, int sum) {
this.size = size;
this.color = color;
this.sum = sum;
}
public String getSize() {
return size;
}
public void setSize(String size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getSum() {
return sum;
}
public void setSum(int sum) {
this.sum = sum;
}
@Override
public String toString() {
return size + color ;
}
}
public class GameDemo {
/**
* 1.定义静态集合存储54张牌
*/
public static List<Card> allCards = new ArrayList<>();
/**
* 2.做牌,定义静态代码块,初始化牌数据
*/
static {
String[] sizes = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String[] colors = {"♠","♥","♣","♦"};
int sum = 0;
for (String size : sizes) {
for (String color : colors) {
sum++;
Card card = new Card(size, color,sum);
allCards.add(card);
}
}
//单独存大小王
Card c1 = new Card("", "小王",++sum);
Card c2 = new Card("", "大王",++sum);
allCards.add(c1);
allCards.add(c2);
System.out.println("新牌:"+allCards);
}
public static void main(String[] args) {
//9.洗牌
Collections.shuffle(allCards);
System.out.println("洗牌:"+allCards);
//10.发牌,定义三个玩家
List<Card> zio = new ArrayList<>();
List<Card> giz = new ArrayList<>();
List<Card> moon = new ArrayList<>();
//11.开始发牌,斗地主留下三张,51张牌,没人17张
for (int i = 0; i < allCards.size()-3; i++) {
Card card = allCards.get(i);
if (i%3==0){
zio.add(card);
}else if (i%3==1){
giz.add(card);
}else if (i%3==2){
moon.add(card);
}
}
//12.拿三张底牌截取成一个子集合
List<Card> lastThreeCards = allCards.subList(allCards.size()-3, allCards.size());
//13.给每个玩家的牌排序
sortCards(zio);
System.out.println("时王:"+zio);
sortCards(giz);
System.out.println("盖茨:"+giz);
sortCards(moon);
System.out.println("月读:"+moon);
System.out.println("三张底牌:"+lastThreeCards);
}
//给每个玩家的牌排序
private static void sortCards(List<Card> cards) {
Collections.sort(cards, new Comparator<Card>() {
@Override
public int compare(Card o1, Card o2) {
return o2.getSum()- o1.getSum();
}
});
}
}
十四、Map集合体系
1.Map集合概述和使用
- Map集合是一种双列集合,每个元素包含两个数据;
- 元素格式:key=value(键值对元素)
- Map集合:键值对集合
1.1集合整体格式
- Collection集合格式:【元素1,元素2,元素3...】
- Map集合格式:{key1=value1,key2=value2,key3=value3...}
1.2特点
- 集合特点:由键决定
- 键key:无序、不重复,无索引;
- 值value:可重复,无要求
- 重复键对应的值,覆盖前面重复键的值
- 键值对都可以为null
1.3Map集合实现类特点
| HashMap | 元素按照键是无序,不重复,无索引,值不做要求;(Map体系一致) |
|---|---|
| LinkedHashMap | 元素按照键是有序,不重复,无索引,值不做要求; |
| TreeMap | 元素按照键是排序,不重复,无索引,值不做要求 |
2.Map集合常用API
/**
* Map常用API
*/
public class MapAPIDemo {
public static void main(String[] args) {
HashMap<String, Integer> maps = new HashMap<>();
maps.put("神极限",100000);
maps.put("黄金头皮屑",100);
maps.put("极限全能X",99);
maps.put("医生",2);
maps.put("机车",2);
maps.put("双人成行",50);
System.out.println(maps);
//2.清空集合
//maps.clear();
//3.判断集合是否为空,空为true
maps.isEmpty();
System.out.println(maps.isEmpty());
//4.根据键获取值:找不到键值,为null
Integer value = maps.get("神极限");
//5.根据键删除元素:会返回删除值
Integer value2 = maps.remove("黄金头皮屑");
//6.是否包含键或值
maps.containsKey("机车");
maps.containsValue(50);
//7.获取全部键集合,返回set集合
Set<String> keys = maps.keySet();
System.out.println(keys);
//8.获取全部值集合,返回Collection集合
Collection<Integer> values = maps.values();
//9.集合元素
maps.size();
//10.合并map集合
HashMap<String, Integer> map1 = new HashMap<>();
map1.put("极限全能X",99);
map1.put("医生",2);
HashMap<String, Integer> map2 = new HashMap<>();
map2.put("全能X",1);
map2.put("医生2",2);
//map2集合全部添加到map1;map2不变
map1.putAll(map2);
}
}
3.Map集合遍历方式
3.1键找值
/**
* 键找值
*/
public class MapDemo01 {
public static void main(String[] args) {
HashMap<String, Integer> maps = new HashMap<>();
maps.put("神极限",100000);
maps.put("黄金头皮屑",100);
maps.put("极限全能X",99);
maps.put("医生",2);
maps.put("机车",2);
maps.put("医生剑士",50);
Set<String> sets = maps.keySet();
for (String set : sets) {
Integer value = maps.get(set);
System.out.println(set+"=="+value);
}
}
}
3.2键值对
/**
* 键值对
*/
public class MapDemo02 {
public static void main(String[] args) {
HashMap<String, Integer> maps = new HashMap<>();
maps.put("神极限",100000);
maps.put("黄金头皮屑",100);
maps.put("极限全能X",99);
maps.put("医生",2);
maps.put("医生剑士",50);
//{机车=2, 极限全能X=99, 黄金头皮屑=100, 神极限=100000, 医生剑士=50, 医生=2}
System.out.println(maps);
/**
* foreach遍历map集合,map集合的键值对元素没有直接类型,不可以直接用
* 调用map方法 entrySet把Map集合转成Set集合形式,
* Set<Map.Entry<String, Integer>> entries = maps.entrySet();
* interface Entry<K,V>
* {(机车=2), (极限全能X=99), (黄金头皮屑=100)}
* 此时可以 foreach遍历
*/
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
for(Map.Entry<String,Integer> entry : entries){
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println(key+"=="+value);
}
}
}
3.3 lambda表达式
/**
* lambda
*/
public class MapDemo03 {
public static void main(String[] args) {
HashMap<String, Integer> maps = new HashMap<>();
maps.put("神极限",100000);
maps.put("黄金头皮屑",100);
maps.put("极限全能X",99);
maps.put("医生",2);
maps.put("医生剑士",50);
//{机车=2, 极限全能X=99, 黄金头皮屑=100, 神极限=100000, 医生剑士=50, 医生=2}
System.out.println(maps);
/**
* 内部原理采用了上方键值对的原理
*/
maps.forEach(new BiConsumer<String, Integer>() {
@Override
public void accept(String key, Integer value) {
System.out.println(key+"=="+value);
}
});
//lambda简化
maps.forEach((key,value) -> System.out.println(key+"=="+value));
}
}
4.案例统计人数
/**
* 统计投票人数
*/
public class MapTest {
public static void main(String[] args) {
String[] selects = {"A","B","C","D"};
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < 80; i++) {
int x = random.nextInt(4);
sb.append(selects[x]);
}
Map<Character, Integer> maps = new HashMap<>();
for (int i = 0; i < sb.length(); i++) {
char c = sb.charAt(i);
if (!maps.containsKey(c)){
maps.put(c,1);
}
Integer value = maps.get(c) + 1;
maps.put(c,value);
}
//{A=25, B=19, C=24, D=16}
System.out.println(maps);
}
}
5.HashMap特点
- Map的一个实现类,特点是由键决定,无序,不重复,无索引;
- HashMap和HashSet底层原理一样,都是哈希表结构,只是HashMap每个元素包含两个值而已
- 实际:Set系列集合底层就是Map实现的,只是Set集合元素只要键数据,不要值数据;
- 依赖hashCode方法和equals方法保证键的唯一;
- 键要存储自定义对象,需要重写hashCode方法和equals方法
- 基于哈希表,增删改查性能都较好
6.LinkedHashMap集合概述和特点
- 由键决定:有序,不重复,无索引
- 有序指保证存储和取出的元素顺序一致;
- 原理:底层结构依然是哈希表,只是每个键值对对元素又额外多了一个双链表的机制记录存储顺序;
7.TreeMap集合概述特点
- 由键决定:可排序,不重复,无索引;
- 可排序:按照键数据的大小默认排序,只能对键排序;
- 注意:TreeMap集合一定要排序,可以默认,也可以指定规则排序
- TreeSet底层基于TreeMap
十五、集合嵌套
/**
* 统计投票人数
* 嵌套循环
*/
public class MapTest02 {
public static void main(String[] args) {
//1.记录每一个学生的选择情况,每人可以选多个
HashMap<Integer, List<String>> data = new HashMap<>();
String[] selects = {"A","B","C","D"};
Random random = new Random();
for (int i = 0; i < 10; i++) {
//2.记录每个人选择
ArrayList<String> select = new ArrayList<>();
int x = random.nextInt(4)+1;
for (int j = 0; j < x; j++) {
select.add(selects[j]);
}
data.put(i,select);
}
//{0=[A, B, C], 1=[A, B, C], 2=[A], 3=[A]}
System.out.println(data);
//3.统计每个景点选择人数
Map<String , Integer> maps = new HashMap<>();
//4.提取所有人选择的值
//[[A, B, C], [A, B, C],[A, B, C]]
Collection<List<String>> values = data.values();
System.out.println(values);
for (List<String> value : values) {
for (String s : value) {
if (maps.containsKey(s)){
Integer v = maps.get(s)+1;
maps.put(s,v);
}else {
maps.put(s,1);
}
}
}
//{A=10, B=7, C=5, D=2}
System.out.println(maps);
//提取统计好的值,比较出人数最多的值
Collection<Integer> valuesTwo = maps.values();
Integer[] nums = new Integer[valuesTwo.size()];
int n=0;
for (Integer integer : valuesTwo) {
nums[n] = integer;
n++;
}
Integer Max = nums[0];
for (int i = 1; i < nums.length; i++) {
if (Max<nums[i]){
Max = nums[i];
}
}
//通过遍历,寻找最大值的的键,输出人多的最想去的地方
Set<Map.Entry<String, Integer>> entries = maps.entrySet();
for (Map.Entry<String, Integer> entry : entries) {
if (entry.getValue()==Max){
System.out.println("想去人数最多的地点是:"+entry.getKey());
}
}
}
}
十六、不可变集合
1.简述
- 集合不可被修改;
- 集合的数据项在创建时被提供,整个生命周期中不可被更改,否则报错。
2.为什么创建不可变集合
- 如果某个数据不可被修改,把它防御性的拷贝到不可变集合中;
- 当集合对象被不可信的库调用时,不可变形式是安全的;
- 敏感信息给第三方看,但不允许修改
3.创建不可变集合
- (JDK9新特性)在List、Set、Map接口中,都存在of方法,都可创建一个不可变的集合
RecordDate:2021/08/14