看完不会我转行!Java集合详解(源码解析)

源码解析仅针对集合、为了个为方便理解才还改了基础的数据结构。
具体的数据结构还需要系统学习以及其代码的实现!

一、集合体系结构

在这里插入图片描述
List系列集合:添加的元素是有序、可重复、有索引
Set系列集合:添加的元素是无序、不重复、无索引

在这里插入图片描述

集合的理解和好处
数组
1.长度开始时必须指定,而且一旦指定,不能更改
2.保存的必须为同一类型的元素
3.使用数组进行增加元素的示意代码 - 比较麻烦

集合
1.可以动态保存任意多个对象,使用比较方便!
2.提供了一系列方便的操作对象的方法: add、remove、set、get等
3.使用集合添加,删除新元素的示意代码- 简洁了

集合主要是两组:单列集合、双列集合
Collection 接口有两个重要的子接口 List set,他们的实现子类都是单列集合
Map 接口的实现子类 是双列集合,存放的 K-V


二、Collection(单列集合)

Collection是单列集合的祖宗接口、它的功能是全部单列集合都可以继承使用的!
说明
1.collection实现子类可以存放多个元素,每个元素可以是Obiect
2.有些Collection的实现类,可以存放重复的元素,有些不可以
3.有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
4.Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的

常见方法:
public boolean add(E e) 把给定的对象添加到当前集合中

1.如果我们要往List系列集合中添加数据,那么方法永远返回true,因为List系列的是允许元素重复的
2.如果我们要往Set系列集合中添加数据,如果当前要添加元素不存在,方法返回true,表示添加成功。如果当前要添加的元素已经存在,方法返回false,表示添加失败。因为Set系列的集合不允许重复。

public void clear() 清空集合中所有的元素
public boolean remove(E e) 把给定的对象在当前集合中删除

1.因为Collection里面定义的是共性的方法,所以此时不能通过索引进行删除。只能通过元素的对象进行删除
2.方法会有一个布尔类型的返回值,删除成功返回true,删除失败返回false;如果要删除的元素不存在,就会删除失败。

public boolean contains(object obj) 判断当前集合中是否包含给定的对象

底层是依赖equals方法进行判断是否存在的。
因此,如果集合中存储的是自定义对象,也想通过contains方法来判断是否包含,那么在javabean类中,一定要重写equals方法

public boolean isEmpty() 判断当前集合是否为空
public int size() 返回集合中元素的个数/集合的长度

1.Collection系列集合三种遍历方式:

ctrl + j 显示所有快捷键的快捷键

①.迭代器 Iterator

快捷键 itit 回车

所有实现了Collection接口的集合类都有一个iterator0方法,用以返回一个实现了lterator接口的对象,即可以返回一个迭代器

Iterator iterator() : 返回迭代器对象,默认指向当前集合的0索引

常用方法:
boolean hasNext(): 判断当前位置是否有元素,有元素返回true,没有元素返回false
E next(): 获取当前位置的元素,并将迭代器对象移向下一个位置。

Iterator<String> it = list.iterator();//xx.iterator()xx取决于你想要取的集合对象名
while(it.hasNext()){
String str = it.next();
System.out.println(str);}

细节注意点:
1.当你指针指向空的时候还在调用,报错NoSuchElementException
2.当你退出while循环后,此时iterator迭代器,指向最后的元素
如果你还想再次遍历,需要重置迭代器,iterator = col.iterator();
3.循环中只能用一次next方法
4.迭代器遍历时,不能用集合的方法进行增加或者删除。
只能使用迭代器提供的remove()方法删除、但无法添加

②.增强for(仅遍历)

快捷键 大写 I 回车
1.本质上底层就是迭代器、JDK5之后出现,是为了简化迭代器的书写
2.所有的单列集合和数组才能用增强for遍历

for(Object o : list){
System.out.println(o);}

o是一个第三方变量,在循环的过程中依次表示集合中的每一个数据
你可以理解成,每次都从 list 当中拿出一个数据给 o,然后输出 o

③.lambda表达式

coll.forEach(s->System.out.println(s));
//一下遍历方法是一个匿名内部类。源码当中是自动便利集合把元素传给accept方法
//自己重写接口的抽象方法之后就能共对数组进行操作
coll.forEach(new Consumer<String>()){
public void accept(String s){
System.out.println(s);}}

2.List集合

List集合特点:
1.有序: 存和取的元素顺序一致
2.有索引: 可以通过索引操作元素
3.可重复: 存储的元素可以重复
4.List集合中的每个元素都有其对应的顺序索引,即支持索引,跟数组差不多意思
5.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
常见方法:
void add(int index,E element): 在此集合中的指定位置插入指定的元素

1.原来索引上的元素会依次往后移

E remove(int index): 删除指定索引处的元素,返回被删除的元素

这里再输入参数的时候同时也会调用到 Collection 类当中的 remove
具体调用哪个取决于你的实参和哪一个方法的形参相同、就先调用哪个!
因为在调用方法的时候,如果方法出现了重戟现象
优先调用,实参跟形参类型一致的那个方法。

E set(int index,E lement): 修改指定索引处的元素,返回被修改的元素
E get(int index): 返回指定索引处的元素

遍历方式:
除 Collection当中提供的三种遍历方式
List还拥有两个属于自己的遍历方式:
列表迭代器遍历(实际上是迭代器的子接口)
它在迭代器的基础上额外添加了一个方法:在遍历过程中、可以添加元素
添加的元素会出现在你所equals的元素后面

Iterator<String> it = list.iterator();//xx.iterator()xx取决于你想要取的集合对象名
while(it.hasNext()){
String str = it.next();
if("淦淦".equals(str)){
lt.add("幸运")//这个位置切记用集合对象去调用、要用迭代器对象调用
}
System.out.println(str);}

普通for循环(因为List集合存在索引)

//size方法跟get方法还有循环结合的方式,利用索引获取到集合中的每一个元素
for (int i = ; i < list.size(); i++) {
//i:依次表示集合中的每一个索引
string s = list.get(i);
System.out.printIn(s);}

3.基础数据结构(仅针对于Java集合)

①.栈(Array)

后进先出、先进后出、进出口相同
数据进入栈模型的过程成为:压 / 进栈;数据离开栈模型的过程成为:弹 / 出栈
放置在栈顶的元素:栈顶元素;放置在栈底的元素:栈底元素

假设:把一个玻璃被子当作栈、把和杯子圆面积大小相同的石头当作元素。
那么当你把石头按顺序放进杯子的时候,最先放进去的石头就在杯子底下,
而最后放进去的石头就在杯子最上方。当你想要取出石头的时候,就必须按照
后进先出、先进后出的方式。这就是 栈!
在这里插入图片描述

②.队列

先进先出、后进后出、进出口不同
数据从后端进入队列模型的过程成为:入队列;数据从前端离开队列模型的过程成为:出队列

假设:把一个管道当作队列、把和管道圆面积大小相同的石头当作元素。
那么当你把石头按顺序从一端放进管道的时候,就可以从另一端取出来。
先进先出、后进后出,这就是队列

③.数组

查询快、增删慢
查询速度快:查询数据通过地址值和索引定位,查询任意数据耗时相同。 (元素在内存中是连续存储的)
删除效率低:要将原始数据删除,同时后面每个数据前移。
添加效率极低:添加位置后的每个数据后移,再添加元素。

④.链表(Linked)

链表中所有的 元素 都称之为 结点
结点中一共有两个空位:一个存放元素具体值,一个存放下一个节点的地址

链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址
链表查询慢,无论查询哪个数据都要从头开始找
链表增删相对数组来说快
在这里插入图片描述
链表的增加
在这里插入图片描述
链表的增加
在这里插入图片描述
单链表
在这里插入图片描述

⑤.树

树的每一个结点都存放着:父节点地址、左子节点地址、右子节点地址、值 没有则会记为 null
每个结点的子节点数量: 任意节点的度 <= 2 称之为 二叉树
根节点为 1 开始称之为 树高

二叉查找树(二叉排序树、搜索树)
特点:
1.每一个节点最多有两个子节点
2.任意节点左子树上的值都小于当前节点
3.任意结点右子树上的值都大于当前节点

二叉树的遍历
①: 前序遍历 中左右
②: 中序遍历 左中右
③: 后序遍历 左右中
④: 层序遍历 一层一层遍历

平衡二叉树(AVL)
任意节点左右子树高度差不超过1、高度平衡
当AVL树添加一个节点之后,不在是一颗平衡二叉树的时候,它能够左旋、右旋
旋转方式
差距只有一层的情况下:
①: 确定支点:从添加的结点开始,不断地往父节点找不平衡的结点
②: 将不平衡的结点做为支点,把支点左旋降级变为左子结点
③: 把原来的右子节点晋升为支点
在这里插入图片描述
差距有多层的情况下:
①: 确定支点:从添加的结点开始,不断地往父节点找不平衡的结点
②: 将不平衡的结点做为支点,将根节点的右侧往左拉
③: 把原来的右子节点变为新的父节点,并把多余的左子节点让出,给已经降级的根节点当右子节点
在这里插入图片描述
右旋同理

需要旋转的四种情况
①: 左左 当根节点左子树的左子树有节点插入,导致二叉树不平衡
②: 左右 当根节点左子树的右子树有节点插入,导致二叉树不平衡
局部左旋、再整体右旋
③: 右右
④: 右左
局部右旋、再整体左旋

⑥.红黑树

是一个二叉查找树,但不是高度平衡的
特有的红黑规则
它比传统二叉树的内容还多一个即:
父节点地址、 左子节点地址、 右子节点地址、 值、 颜色

红黑规则
①: 每一个节点或是红色的,或者是黑色的
②: 根节点必须是黑色
③: 如果一个节点没有子节点或者父节点,则该节点相应的指针属性值为Nil,这些Nil视为叶节点,每个叶节点(Nil)是黑色的
④: 如果某一个节点是红色,那么它的子节点必须是黑色(不能出现两个红色节点相连的情况)
⑤: 对每一个节点,从该节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点;

红黑树添加节点规则
①: 默认颜色为红色(效率高)
在这里插入图片描述
旋转的时候不需要考虑Nil结点、直接旋转。


4.ArrayList集合(源码解析)

Debug测试源码过程可以到设置当中,Java 把 Enable alternative view... 选线取消打勾 Hide null elements 是看隐藏的空数组也取消

说明
1.利用空参创建的集合,在底层创建一个默认长度为0的数组
2.添加第一个元素时,底层会创建一个新的长度为10的数组
3.存满时,会扩容1.5倍
在这里插入图片描述
♿♿♿ArrayList源码♿♿♿
在这里插入图片描述ArrayList扩容机制
1.ArrayList中维护了一个Object类型的数组elementData[] 由于是Object所以什么类型都能放.
transient Object[] elementDate::transient 表示瞬间、短暂的,表示属性不会被序列化
2.当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍
3.如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容则直接扩容elementData为1.5倍
注:
底层源码当中,将旧容量加上就容量右移 1 位。比如10的二进制 1010 右移变成 0101 也就是5 此时,数组等于扩容了1.5倍但并不是第一次就采用扩容机制
可以追溯到ArrayList 的 grow 方法当中查看扩容源码
在源码中最后使用了 Arrays.copyOf 来达成赋值,这个方法会保留原来数组中存在的元素


5.LinkedList集合(源码解析)

说明
1.LinkedList底层实现了双向链表双端队列特点
2.可以添加任意元素(元素可以重复),包括null
3.线程不安全,没有实现同步

♿♿♿LinkedList源码♿♿♿
在这里插入图片描述

双链表在这里插入图片描述


6.迭代器 Iterator (源码解析)

♿♿♿Iterator源码♿♿♿
在这里插入图片描述


7.泛型深入💢

说明: 泛型是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查
泛型的格式: <数据类型>
注意: 泛型只能支持引用数据类型

如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
此时可以往集合添加任意的数据类型。带来一个坏处:我们在获取数据的时候,无法使用它的特有行为。

因此、才推出了泛型可以在添加数据的时候就把类型进行统一,而且我们在获取数据的时候也不省得进行强转,非常的方便

ArrayList list = new ArrayList();
list.add(123);list.add("aaa");list.add(new Student("张三",123));
//分别假如三种不同的数据类型,因为没有泛型约束的情况下,默认类型为(Object o)
Iterator it = list.iterator();
while(it.hasNext()){
Student str = (Student)it.next();
//obj.length(); 此时在默认Object数据类型的情况下,无法调用子类数据类型的方法
str.length();//比如这里的String中的length()方法
System.out.println(str);
}

泛型的好处:
1.统一数据类型
2.把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来

深入细节
1.Java中的泛型是伪泛型
只有在编译阶段它会对你的类型进行判断,当你编译变成class文件之后,泛型的代码就消失了。
称之为(泛型的擦除)。当你通过了判断,在集合之中你的数据依然以Object类型存在,只有取出的时候才会再次进行判断。
2.泛型中不能写基本数据类型(基本数据类型无法变成Object、必须写包装类)
3.指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
4.如果不写泛型,类型默认是object

泛型可以写在 类后面、方法上面、接口后面 通常字母为 T、 E、 K、 V
泛型类:在编写一个类的时候、如果不确定类型、那就可以定义这个类为泛型类、

public class MyArrayList<E>{
Object[] obj = new Object[10];
int size;
public boolean add(E e){//E创建泛型类时用的E e是大E这个类型的变量名
obj[size] = e;
size++; return true;
}
public E get(int index){//这里因为方法需要返回,但不确定类型所以也是用E
return (E)obj[index];}//由于数组名是Object类型的、所以需要强转成E类型
}

泛型方法:方法中形参类型不确定,可以使用类名后面定义的泛型< E >
泛型方法 < E > 或者写在修饰符后面
:使用类名后面定义的泛型 → 所有的方法都能够使用
:在方法申明上定义自己的泛型→ 只有本方法能使用

public <E> boolean add(E e){//E创建泛型类时用的E e是大E这个类型的变量名
obj[size] = e;
size++; return true;
}

泛型接口:接口中方法中类型不确定,可以使用接口名后面定义的泛型
:实现类给出具体类型
在这里插入图片描述
:实现类延续泛型、创建对象时再确定

泛型的继承和通配符:
1. 泛型不具备继承性
假如你定义三个类相互继承、将子类作为泛型名称。当你调用该泛型方法的时候,是无法填入拥有继承关系的类名当作泛型中的数据使用。

2.通配符
我们知道泛型< E >可以添加任意引用数据类型,那当我只允许某三个引用数据类型当中任意的情况下。我们就需要用到通配符
①: ? extends E: 表示可以传递 E 或者 E 所有的子类类型
②: ? super E: 表示可以传递 E 或者 E 所有的父类类型
以上两种方法当中的 E 皆为具体的引用数据类型。比如 class Father extends GrandFather. 可以根据需求将 E 替换成 Father 或者 GrandFather

应用场景:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符泛型的通配符:
关键点: 可以限定类型的范围。


8.Set集合

Set集合特点
无序: 存取顺序不一致
不重复: 可以去除重复
无索引: 没有带索引的方法,所以不能使用普通for循环遍历,也不能通过索引来获取元素

按照哈希值来排序存放位置的

set集合的实现类
HashSet: 无序、不重复、无索引
LinkedHashSet: 有序、不重复、无索引
TreeSet: 可排序、不重复、无索引

Set接口中的方法上基本上与Collection的API一致


9.HashSet底层原理

HashSet集合底层采取哈希表存储数据
哈希表是一种对于增删改查数据性能都较好的结构

JDK8之前: 数组 + 链表
JDK8开始: 数组 + 链表 + 红黑树

哈希值:
1.根据hashCode方法算出来的int类型的整数
2.该方法定义在obiect类中,所有对象都可以调用,默认使用地址值进行计算
3.一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

对象的哈希值特点:
1.如果没有重写hashCode方法,不同对象计算出的哈希值是不同的
2.如果已经重写hashcode方法,不同的对象只要属性值相同,计算出的哈希值就是一样的
重写hashcode:在信息类里面右键 Generate equals() and hashCode();
这个选项表明:
第一次选当XXX属性(你选择的属性)值相同,在使用equals的时候返回true
第二次选当XXX属性(你选择的属性)值相同,在计算hashCode()时返回相同结果
3.在小部分情况下,不同的属性值或者不同的地址值计算出来的哈希值也有可能一样。 (哈希碰撞)在这里插入图片描述
0.75在底层源码中名字叫做阈值。它是提前检测数组是不是快要满了的!
当你每一个链表的元素到达了8个、同时你整张表的元素也到达了64个。此时表会扩容

解析: 当你添加元素、会先得到hash值将其转换成索引。之后到表中存放,看看表中这个位置有没有一样的元素。如果有一样的元素、那就使用equals比较。至于是比较地址还是比较值、那是取决于程序员有没有重写。假如元素equals相同那就放弃添加,如果不相同、但是索引值相同,那就添加然后放到原先元素的后面,就是一张邻接表的样子。

HashSet的遍历方式是: 按数组顺序,非null元素进入当前链表或者红黑树遍历。

10.LinkedHashSet底层原理

有序、不重复、无索引
这里的有序指的是保证存储和取出的元素顺序一致
原理: 底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。按照每一个元素进入table的顺序,让两个元素之间用双向链表连接起来。这样就能保证顺序了!
在这里插入图片描述


11.TreeSet底层原理

1.不重复、无索引、可排序
2.可排序:按照元素的默认规则(由小到大)排序
3.TreeSet集合底层是基于红黑树的数据结构实现排序的,增删改查性能都较好

1.对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序
2.对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序(字符串按照单个字符逐一比较)

自定义类需要用TreeSet排序时、有两种方式来自定义排序规则
①: 自定义类 实现 Comparable 接口,重写里面的抽象方法。
在这里插入图片描述
元素加入的顺序也是按照红黑树的红黑规则
②: 创建TreeSet对象时候,传递比较器Comparator指定规则
匿名内部类重写,在方法中调用的compareTo是默认的比较方法,即ascll码比较
默认使用第一种,如果第一种不能满足需求,便使用第二种。
在这里插入图片描述
返回值特点:
负数: 表示当前要添加的元素是小的,存左边
正数: 表示当前要添加的元素是大的,存右边
0 : 表示当前要添加的元素已经存在,舍弃


12.集合的选用

1.如果想要集合中的元素可重复
ArrayList集合,基于数组的(用的最多)
2.如果想要集合中的元素可重复,而且当前的增删操作明显多于查询LinkedList集合,基于链表
3如果想对集合中的元素去重
HashSet集合,基于哈希表的(用的最多)
4.如果想对集合中的元素去重,而且保证存取顺序LinkedHashSet集合,基于哈希表和双链表,效率低于HashSet
5.如果想对集合中的元素进行排序
TreeSet集合,基于红黑树。后续也可以用List集合实现排序


三、Map(双列集合)

双列集合的特点: 每一次传输都是一对数据,称之为(键值对)
键: Key 值: value
①: 双列集合一次需要存一对数据,分别为键和值
②: 键不能重复,值可以重复
③: 键和值是一一对应的,每一个键只能找到自己对应的值
④: 键 + 值这个整体我们称之为 键值对 或者 键值对对象,在Java中叫做 Entry对象

Map常用API:
Map是双列集合的顶层接口,它的功能是全部双列集合都可以继承使用的

V put(K key,V value): 添加元素

在添加数据的时候,如果键不存在,那么直接把键值对对象添加到map集合当中,返回null
在加数据的时候,如果键是存在,那么会把原有的键值对对象覆盖,会把被覆盖的值进行返回

V remove(Object key): 根据键删除键值对元素
void clear(): 移除所有的键值对元素
boolean containsKey(object key): 判断集合是否包含指定的键
boolean containsValue(obiect value): 判断集合是否包含指定的值
boolean isEmpty(): 判断集合是否为空
int size(): 集合的长度,也就是集合中键值对的个数

Map的遍历方式:
①: 键找值

Map map = new HashMap();
        map.put("幸运","淦淦");
        map.put("开心","淦淦1");
        map.put("健康","淦淦2");
        Set set = map.keySet();//来个单例集合存储一下key
        for (Object s : set) {
            System.out.println(s);

②: 键值对
entrySet(): 可以理解为把entry对象放到Set集合当中。
在这里插入图片描述
③: Lambda表达式
forEach直接重写、在此不再赘述

1.HashMap

无序、不重复、无索引
①: HashMap是Map里面的一个实现类
②: 没有额外需要学习的特有方法,直接使用Map接口里面的方法就完事
③: HashMap跟HashSet底层原理是一模一样的,都是哈希表结构
区别在于: 双列集合多了一个键、当键一样的时候就会带着 value 进行覆盖。如果键不一样,就会挂在下面、长度超过8、数组长度>=64转红黑树就一模一样了。

1.HashMap底层是哈希表结构的
2.依赖hashCode方法和equals方法保证键的唯一、值无所谓
3.如果键存储的是自定义对象,需要重写hashCode和equals方法
如果值存储自定义对象,不需要重写hashCode和equals方法

Node对象(数组+链表)
在这里插入图片描述
TreeNode对象(红黑树)
在这里插入图片描述

HashMpa虽然有红黑树、但它底层默认是用哈希值的大小关系来创建红黑树,所以你不需要实现Compareable接口或者传递比较器对象


2.LinkedHashMap的特点

有序(存取顺序一致)、不重复、无索引
这里的有序指的是保证存储和取出的元素顺序一致
原理: 底层数据结构是依然哈希表,只是每个键值对元素又额外的多了一个双链表的机制记录存储的顺序。而且当键相同的时候 value也是直接覆盖。并不像LinkedHashSet一样是舍弃添加

2.TreeMap的特点

由键决定特性: 不重复、无索引、可排序
TreeMap跟TreeSet底层原理一样,都是红黑树结构的
可排序: 对键进行排序
注意: 默认按照键的从小到大进行排序,t也可以自己规定键的排序规则

代码书写两种排序规则:
1.实现Comparable接口,指定比较规则
2.创建集合时传递Comparator比较器对象,指定比较规则。
两种都写的话以第二种为准、毕竟匿名内部类是运行时最后触发的重写

**按要求遍历格式:**在这里插入图片描述

3.方法列表解释

在这里插入图片描述
方法名():<方法类型> ↑:表示该方法是继承或者接口的重写
→:表示方法在这个接口或者类当中
粉色m:方法 ; 黄色f:属性、常量 ; 蓝色c:类 ; 绿色I: 接口

HashMap 和 TreeMap 是有出现重复值可以不覆盖的方法的,只不过一般用不到,源码已经替你重写好了

4.如何选择使用哪个集合?

HashMap LinkedHashMap TreeMap
默认: HashMap(效率最高)
保证存取有序: LinkedHashMap
保证排序: TreeMap

shift + F6 批量改名


四、不可变集合

不可变集合: 不可以被修改的集合。(长度、内容都不可变)
1.如果某个数据不能被修改,把它防御性地拷贝到不可变集合中是个很好的实践
2.当集合对象被不可信的库调用时,不可变形式是安全的

List<String> list = List.of("张三","李四","王五","赵六");
//形参的数据类型是可变参数

以上代码一旦创建完毕,是无法进行修改的,只能进行查询操作!
当我们要获取一个不可变的Set集合时,里面的参数一定要保证唯一性
但是我们知道,使用双列集合的时候,不可能使用两个可变参数因此:

//JDK8.0以前
Map<Object, Object> map = Map.ofEntries(hashMap.entrySet().toArray(new Map.Entry[0]));
//JDK8.0以后
Map<String,String> map = Map.copyOf(hashMap);

1.KeySet()和entrySet()

2.Stream流

结合了Lambda表达式,简化集合、数组的操作
Stream流的使用步骤:
①: 先得到一条Stream流(流水线),并把数据放上去
在这里插入图片描述

②: 利用Stream流中的API进行各种操作
过滤、转换(中间方法):方法调用完毕后、还可以调用其他方法
统计、打印(终结方法):方法调用完毕之后、不能调用其他方法,是最后一步!

//=============================单列集合===============================
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a","b","c","d");
list.stream().forEach(s -> System.out.println(s));//直接打印
//=============================双列集合===============================
//keySet()只能够得到 key
hashMap.keySet().stream().forEach(s -> System.out.println(s));
//entrySet()能够得到key 和 value
hashMap.entrySet().stream().forEach(s -> System.out.println(s));
//==============================数组================================
int[] arr = {1,2,3,4,5,6,7,8,9};
Array.stream(arr).forEach(s -> System.out.println(s));
//stream里面可以选择不同的数据类型、来针对不同的数组的输出!
//=============================零散数据===============================
stream.of(1,2,3,4,5).forEach(s -> System.out.println(s));
stream.of("a","b","c","d").forEach(s -> System.out.println(s));
你可以在stream.of()里面写引用数据类型、如果是基本数据类型肯定会报错

在这里插入图片描述
注意1: 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2: 修改Stream流中的数据,不会影响原来集合或者数组中的数据

在这里插入图片描述

3.方法引用

引用静态方法、成员方法、构造方法、其他调用方式
把已经有的方法拿过来用、但顾总函数式接口中的抽象方法的方法体
方法引用的条件:
①: 引用处必须是函数式接口
②: 被引用的方法必须已经存在
③: 被引用方法的形参和返回值需要跟抽象方法保持一致
④: 被引用方法的功能要满足当前需求

方法引用符: “::”
引用静态方法:
①:类名::方法名
引用成员方法:
格式:对象::成员方法
①: 其他类: 其它类对象::方法名
②: 本类: this::方法名 (static类当中是不存在this的,如果你要在main方法当中引用方法、可以new 一个main方法的类名来引用方法)
③: 父类: super::方法名

图解
在这里插入图片描述
引用构造方法:
格式: 类名::new
例如: Student::new
图解
在这里插入图片描述
其他调用方式:
①: 实用类名引用成员方法
类名::成员方法
②: 引用数组的构造方法
数据类型[ ]::new


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值