当前位置:首页 > 资讯 > 正文

集合进阶(一)

ArrayList集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常有用,为了满足不同的业务场景需求,Java还提供了许多不同的特点的集合

  1. 集合体系:单列集合(Collection)、双列集合(Map)

  2. Collection集合体系

  3. Collection

    • Collection的常用方法

    • Collection的遍历方式

  4. List集合

    • 特点,特有方法
    • 遍历方式
    • ArrayList集合的特点及底层原理
    • LinkedList集合的底层原理和常用方法
  5. Set集

    • 特点
    • HashSet集合的特点及底层原理
    • LinkedHashSet集合的特点及底层原理
    • TreeSet的特点及底层原理
  6. Collection集合的使用总结,集合的并发修改异常问题

  7. Collection的其他相关知识

    • 前置知识:可变参数
    • Collections
    • 综合案例
  8. Stream流

    • 获取Stream流
    • Stream提供的中间终结方法
    • Stream提供的常用终结方法

1. Collection 单列集合

2. Map 双列集合

1

Collection代表单列集合:每个元素(数据)只包含一个值

2

Map代表双列集合,每个元素包含两个值(键值对)

3

  • List系列集合:添加的元素是有序,可重复、有索引
    1. ArrayList、LinkedList:有序、可重复、有索引
  • Set系列集合:添加的元素是无序、不重复、无索引
    1. HashSet:无序、不重复、无索引
    2. LinkedHashSet:有序、不重复、无索引
    3. TreeSet:按照大小默认升序排序,不重复、无索引

Collection的常用方法

方法名说明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的遍历方式

  1. 迭代器
  2. 增强for
  3. lambda表达式
迭代器
  • 迭代器是用来遍历集合的专用方式(数组没有迭代器,在Java中迭代器的代表是Iterator)

Collection集合获取迭代器的方法

方法名说明Iterator iterator返回集合中的迭代器对象,该迭代器默认指向当前集合的第一个元素

Iterator迭代器的常见方法

方法名说明boolean hasNext()询问当前位置是否有元素存在,存在返回true,不存在返回falseE next()获取当前位置的元素,并同时将迭代器对象指向下一个元素处
 
增强for循环

格式

 
  • 增强for可以用来遍历集合或数组
  • 增强for遍历集合,本质就是迭代器遍历集合的简化写法
 
lambda表达式遍历集合
  • 得益于JDK 8,开始的新技术lambda表达式,提供了一种更简单更直接的方式来遍历集合

需要使用Collection的如下方法完成

方法名说明default void forEach(Consumer <? super T> action)结合lamdba遍历集合
 
案例:遍历集合中的自定义对象

需求:展示多部电影

分析:

  1. 每部电影都是一个对象,多部电影要用集合装起来
  2. 遍历集合中的3个电影对象,输出每部电影的详情信息
 
 
  1. 特点、特有方法
  2. 遍历方式
  3. ArrayList集合的底层原理
  4. LinkedList集合的底层原理

·

List集合的特有方法

List集合因为支持索引,所以多了许多与索引相关的方法,当然,Collection的功能List也继承了

方法名说明void add(int index,E element)在此集合中的指定位置插入指定的元素E remove(int index)删除指定索引处的元素,返回被删除的元素E set(int index,E element)修改指定索引处的元素,返回被修改的元素E get(int index)返回指定索引处的元素
 
List集合支持的遍历方式
  1. for循环(因为List集合有索引)
  2. 迭代器
  3. 增强for循环
  4. lamdba表达式
 
ArrayList集合的底层原理
  • 基于数组实现的(查询快,增删慢)
  • 数组的特点
    1. 删除效率低:可能需要把后面很多的数据进行前移
    2. 添加效率极低:可能需要把后面很多的数据后移,在添加元素,或者也可能需要进行数组的扩容
  1. ArrayList集合底层存数据的原理
    • 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
    • 添加第一个元素时,底层会创建一个新的长度为10的数组
    • 存满时,会扩容1.5倍
    • 如果一次添加多个元素,1.5倍还放不下,则新创建的数组的长度以实际为准
  2. ArrayList集合适合的应用场景
    • ArrayList适合:根据索引查询数据,比如根据随机索引取数据(高效),或者数据量不是很大时!
    • ArrayList不适合:数据量大的同时,又要频繁的进行增删操作!
LinkedList集合的底层原理
  • 基于双链表实现的
  1. 什么是链表?有啥特点
    • 链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址

1

链表的特点:

  1. 查询慢,无论查询哪个数据都要从头开始
  2. 链表增删相对(数组)快
单向链表

2

双向链表

2

LinkedList新增了许多首尾操作的特有方法
方法名称说明Public void addFirst(E e)在该列表开头插入指定的元素Public void addLast(E e)将指定的元素追加到此列表的末尾Public E getFirst()返回此列表的第一个元素Public E getLast()返回此列表的最后一个元素Public E removeFirst()从此列表中删除并返回第一个元素Public E removeLast()从此列表中删除并返回最后一个元素
LinkedList的应用场景之一:可以用来设计队列(叫号系统、排队系统)

队列:先进先出,后进后出

只是在首尾增删元素,用LinkedList来实现很合适!

 
LinkedList的应用场景之一:可以用来设计栈

栈:后进先出,先进后出

  • 数据进入栈模型的过程称为:压/进栈(push)

  • 数据离开栈模型的过程称为:弹/出栈(pop)

 
 
  1. 特点
  2. HashSet集合的底层原理
  3. LinkedHashSet集合的底层原理
  4. TreeSet集合

4

Set系列集合特点:无序:添加数据的顺序和获取数据顺序不一致,不重复;无索引
  • HashSet:无序,不重复,无索引
  • LinkedHashSet:有序,不重复,无索引
  • TreeSet:排序,不重复,无索引
HashSet集合的底层原理
  1. 为什么添加的元素无序、不重复、无索引?
  2. 增删改查数据有什么特点,适合什么场景?

哈希值:
  • 就是一个int类型的数值,java中每个对象都有一个哈希值
  • java中所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值
对象哈希值的特点:
  • 同一个对象多次调用hashCode()方法,返回的哈希值是相同的
  • 不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞),int(-21亿多 - 21亿多)
HashSet的底层原理:
  • 基于哈希表实现
  • 哈希表是一种增删改查数据,性能都较好的数据结构
哈希表:
  • JDK8之前,哈希表 = 数组 + 链表
  • JDK8开始,哈希表 = 数组 + 链表 + 红黑树
JDK8之前HashSet集合的底层原理,基于哈希表:数组 + 链表

5

  1. 创建一个默认长度为16的数组,默认加载因子为0.75,数组名为table
  2. 使用元素的哈希值对数组的长度求余,计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入该数据
  4. 如果不为null,表示当前位置有元素,则调用equals方法比较,相等则不存,不相等,则存入数组
    • JDK8之前,新元素存入数组,占老元素位置,老元素挂下面
    • JDK8开始之后,新元素直接挂在老元素下面
JDK8开始之后,HashSet集合的底层原理,基于哈希表:数组 + 链表 + 红黑树

6

  • JDK8开始,当链表长度超过8,且数组长度 >= 64时,自动将链表转成红黑数
  • 小结:JDK8开始后,哈希表中引入了红黑树后,进一步提高了操作数据的性能
了解一下数据结构(树)
  • 红黑树,就是可以自平衡的二叉树
  • 红黑树是一种增删改查数据性能相对都较好的结构
深入理解HashSet集合去重复的机制

HashSet集合默认不能对内容一样的两个不同对象去重复

  • 比如内容一样的两个学生对象存入到HashSet集合中去,HashSet集合是不能去重的

如何让HashSet集合能够实现对内容一样的两个不同对象也能去重复?

  • 如果希望Set集合认为2个内容一样的不同对象是重复的,必须重写对象的hashCode() 和 equals()方法
 
LinkedHashSet:有序、不重复、无索引
  • 依然是基于哈希表(数组、链表、红黑树)实现的
  • 但是,它的每个元素都额外的多了一个双链表的机制记录它前后的位置

8

TreeSet
  • 特点:不重复,无索引,可排序(默认升序,按照元素的大小,由小到大排序)
  • 底层是基于红黑数实现的排序
注意:
  • 对于数值类型:Integer,Double,默认按照数值本身的大小进行升序
  • 对于字符串类型:默认按照首字符的编号升序排序
  • 对于自定义类型如:Student 对象,TreeSet默认是无法直接排序的

自定义排序规则

  • TreeSet集合存储自定义类型的对象时,必须指定排序规则

方式一:

让自定义的类实现Comparable接口,重写里面的CompareTo方法,来指定比较规则

 

方式二:

通过调用TreeSet集合的有参构造器,可以设置Comparator对象(比较器对象)

方法名Public TreeSet(Comparator < ? super E > comparator)
 
Collection集合的之一总结,集合的并发修改异常问题
  1. 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
    • 用ArrayList集合(有序、可重复、有索引),底层基于数组(常用)
  2. 如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
    • 用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的
  3. 如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
    • 用HashSet集合,底层基于哈希表实现的(常用)
  4. 如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快
    • 用LinkedHashSet集合(有序、不重复、无索引),底层基于哈希表和双链表
  5. 如果要对元素进行排序,也没有重复元素需要存储,且希望增删改查都快
    • 用TreeSet集合,基于红黑树实现
  • 使用迭代器遍历集合,又同时在删除集合中的数据时,程序就会出现并发修改异常的错误
  • 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常。
 

怎么保证遍历集合时删除数据不出bug?

  • 使用迭代器遍历集合,使用迭代器自己的删除方法删除数据即可
  • 使用for循环遍历
    1. 从前往后遍历,但删除元素后做i–操作
    2. 倒着遍历集合并删除
 
 
  • 前置知识:可变参数
  • Collections
  • 综合案例
可变参数
  • 就是一种特殊的形参,定义在方法、构造器的形参列表里,格式是:
可变参数的特点和好处
  • 特点:可以不传数据给形参,可以传一个数据或者多个数据给形参,也可以传一个数组给形参
  • 好处:常常用来灵活的接收数据
 
可变参数的注意事项
  • 可变参数在方法内部就是一个数组
  • 一个形参列表中可变参数只能有一个
  • 可变参数必须放在形参列表的最后面
  • 是一个用来操作集合的工具类

Collections提供的常用静态方法

方法名称说明Public static boolean addAll(collection < ? super T > c,T … elements )给集合批量添加数据Public static void shuffle(List<?> list)打乱List集合中的元素顺序Public static void sort(List list)对list集合中的元素进行升序排序Public static void sort(List list,Comparator< ? super T > c )对list集合中元素,按照比较器对象指定的规则进行排序
案例:斗地主游戏
  • 总共有54张牌
  • 点数:“3”,“4”,“5”,“6”,“7”,“8”,“9”,“10”,“J”,“Q”,“K”,“A”,“2”
  • 花色:“♠”,“♥”,“♣”,“♦”
  • 大小王:“☺”,“☹”
  • 斗地主:发出51张牌,剩下3张作为底牌

分析实现:

  1. 在启动游戏房间的时候,应该提前准备好54张牌
  2. 接着,需要完成洗牌,发牌,对牌排序,看牌
 
 
  1. 概述
  2. 常用方法
  3. 遍历方式
  4. HashMap
  5. LinkedHashMap
  6. TreeMap
  7. 补充知识:集合的嵌套
1.认识Map集合
  • Map集合称为双列集合,格式:{ key1 = value1 , key2 = value2,key3 = value3,…},一次需要存一对数据作为一个元素
  • Map集合的每个元素"key = value " 称为一个键值对 | 一个键值对对象 | 一个Entry对象,Map集合也被叫做“键值对集合”
  • Map集合的所有键是不允许重复的,但值可以重复,键和值是 一 一对应的,每一个键只能找到自己对应的值
Map集合在什么业务场景下使用

比如购物车里面的商品列表 { 商品1 = 2 , 商品2 = 3, 商品3 = 2},需要存储一 一对应的数据时,就可以考虑使用Map集合

Map集合体系

8

Map集合体系的特点

注意:Map系列集合的特点,都是由键决定的,值只是一个附属品,值是不做要求的

  • HashMap(由键决定特点):无序、不重复、无索引;(用的最多)
 
  • LinkedHashMap(由键决定特点):有序,不重复,无索引
 
  • TreeMap(由键决定特点):按照键的大小默认升序,不重复,无索引
 
Map的常用方法

为什么要先学习Map的常用方法?

  • Map是双列集合的祖宗,它的功能是全班双列集合都可以继承过来使用的
Map的常用方法说明Public V put (K key , V value)添加元素Public int size()获取集合的大小Public void clear()清空集合Public boolean isEmpty()判断集合是否为空,为空返回true,反之Public V get(Object key)根据键获取对应值Public V remove(Object key)根据键删除整个元素,并返回被删除的元素对应的值Public boolean containsValue(Object value)判断是否包含某个值Public Set keySet()获取全部键的Set集合Public Collection values()获取Map集合的全部值的Collection的集合
 
Map集合的遍历方式
  1. 键找值:先获取Map集合全部的键,在通过遍历键来找值
  2. 键值对:把"键值对"看成一个整体进行遍历(难度较大)
  3. lambda:JDK8开始之后的新技术(非常的简单)
Map集合的遍历方式一:键找值,需要用到Map的如下方法
方法名称说明Public Set keySet()获取所有键的集合Public V get(Object key)根据键获取其对应的值
 
Map集合的遍历方式二:键值对

Map提供的方法

方法名说明Set < Map.Entry<K,V> > entrySet()获取所有“键值对”的集合

Map.Enrty提供的方法

方法名说明K getkey()获取键V getValue()获取值
 
Map集合的遍历方式三:Lambda

需要用到Map的如下方法

方法名说明default void forEach(BiConsumer< ? super K , ? super V > action )结合Lambda遍历Map集合
 
Map集合的案例:统计投票人数

需求:某个班级100名学生,现在需要组织秋游活动,班长提供了四个景点(A,B,C,D),每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多

分析:

  1. 将100个学生选择的数据拿到程序中去,[A,A,B,A,B,C,D…]
  2. 准备一个Map集合用于存储统计结果,Map<String,Integer>,键是景点,值是票数
  3. 遍历100个学生选择的景点,每遍历一个景点,就看Map集合中是否存在该景点,不存在在存入“ 景点 = 1 ”,存在则其对应的值 +1
 
HashMap集合的底层原理
  • HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的
  • 实际上Set系列的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已
 
HashMap底层是基于哈希表实现的
  • JDK8之前,哈希表 = 数组 + 链表
  • JDK8开始之后,哈希表 = 数组 + 链表 + 红黑数
  • 哈希表是一种增删改查数据,性能都较好的数据结构
  • 无序、不重复、无索引(由键决定特点)
  • HashMap的键依赖hashCode()方法和equals()方法保证键的唯一
  • 如果键存储的是自定义类型的对象,可以通过重写hashCode()和equals()方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的
LinkedHashMap集合的底层原理
  • 底层数据结构依然是基于哈希表实现的,只是每个键值对元素,又额外的多了一个双链表机制,记录元素顺序(保证有序)
  • 实际上,原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap
  • 有序、不重复、无索引
TreeMap
  • 特点“不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序)
  • 原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑数实现的排序
TreeMap集合同样也支持两种方式来指定排序规则
  1. 让类实现Comparable接口,重写比较规则

  2. TreeMap集合有一个有参构造器,支持创建Comparator比较器对象,以便用来指定比较规则

需求:要求在程序中记住如下省份和其对应的城市信息,记录成功后要求可以查询出湖北省的城市信息

分析:定义一个Map集合,键用来表示省份名称,值表示城市名称

注意:城市有多个,可以根据"湖北省"这个键获取对应的值展示即可

 
 
  1. 认识Stream
  2. Stream的常用方法
什么是Stream?
  • 也叫Stream流,是JDK8开始新增的一套API(java.unit.Stream.*),可以用于操作集合或数组的数据
  • 优势:Stream流大量的结合力Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作集合或者数组中的数据,代码更简洁,可读性更好
体验Stream流

需求:把集合中所有以“张”开头,且是3个字的元素存储到一个新的集合中去

 
Steram流的使用步骤

9

常用方法
  1. 获取Stream流
  • 获取集合的Stream流

Collection提供的如下方法

方法名说明default Stream stream()获取当前集合对象的Stream流
  • 获取数组的Stream流

Arrays类提供的如下方法

方法名说明Public static Stream Stream( T[] array )获取当前数组的Stream

Stream类提供的如下方法

方法名说明Public static Stream of( T…values)获取当前接受数据的Stream流
 
  1. Stream流常见的中间方法
  • 中间方法指的是调用完成后会返回新的Stream流,可以继续使用(链式编程)
方法名说明Stream filter(Predicate< ? super T> predicate)用于对流中的数据进行过滤Stream sorted()对元素进行升序排序Stream sorted(Comparator <? super T> comparator)按照指定规则排序Stream limit(long maxSize)获取前几个元素Stream skip(long n)跳过前几个元素Stream distinct()去除流中重复的元素 Stream map( Function < ? super T , ? extends R > mapper )对元素进行加工,并返回对应的新流Static Stream concat(Stream a,stream b)合并a和b两个流为一个流
 
  1. Stream流常见的终结方法
  • 终结方法指的是调用完成后,不会返回新的Sream流,没法继续使用流了

Stream提供的常见终结方法

方法名说明void forEach(Consumer action)对此流运算后的元素执行变量long count()统计此流运算后的元素个数Optional max(Comparator < ? super T > comparator )获取此流运算后的最大值元素Optional min(Comparator < ? super T > comparator )获取此流运算后的最小值元素
  • 收集Stream流:就是把Stream流操作后的结果转回到集合或数组中去返回

Stream流:方便操作集合 / 数组的手段;集合 / 数组:才是开发中的目的

Stream提供的常用终结方法

方法名说明R collect( Collector collector)把流处理后的结果收集到一个指定的集合中去Object[] toArray()把流处理后的结果收集到一个数组中去

Collector工具类提供了具体的收集方式

方法名说明Public static Collector toList()把元素收集到List集合中去Public static Collector toSet()把元素收集到Set集合中去Public static Collector toMap(Function KeyMapper,Function ValueMapper)把元素收集到Map集合中去

最新文章