Java集合---保姆级别

集合

集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。

集合体系结构

  • 单列集合(Collection):代表单列集合,每个元素(数据)只包含一个值
  • 双列集合(Map):代表双列集合,每个元素包含两个值(键值对)

Collection集合特点

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

迭代器概述

  • 迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator

Collection集合获取迭代器的方法

方法名称

说明

Iterator<E> iterator()

返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素

Iterator迭代器中的常用方法

方法名称

说明

boolean hasNext()

询问当前位置是否有元素存在,存在返回true,不存在返回false。

E next()

获取当前位置的元素,并同时将迭代器对象指向下一个元素处。

增强for循环

格式:for(元素的数据类型 变量名:数组或者集合){ }

  • 增强for可以用来遍历集合或者数组。
  • 增强for遍历集合,本质就是迭代器遍历集合的简化写法。

Lambda表达式遍历集合

得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。

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

方法名称

说明

default void forEach(Consumer action)

结合Lambda遍历集合

List集合

特点、特有方法

  • Lis系列集合:添加的元素是有序、可重复、有索引
    • ArrayList、LinkedList:有序、可重复、有索引

方法名称

说明

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. Lambda表达式

ArrayList集合的底层原理

  • 基于数组实现的。
  • 数组的特点
    • 查询速度快注意:是根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
    • 删除效率低:可能需要把后面很多的数据进行前移。
    • 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
  • ArrayList集合的底层原理

  1. 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
  • ArrayList集合适合的应用场景

  • ArrayList适合:根据索引查询数据,比如根据随机索引取数据(高效)或者数据量不是很大时!
  • ArrayList不适合:数据量大的同时,又要频繁的进行增删操作!

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的应用场景之二:可以用来设计栈

栈的特点:后进先出,先进后出

Set集合

Set系列集合特点无序,添加数据的顺序和获取出的数据顺序不一致;不重复,无索引

HashSet:无序、不重复、无索引。

LinkedHashSet:有序、不重复、无索引。

TreeSet:排序、不重复、无索引。

注意:

Set要用到的常用方法,基本上就是Collectio提供的!!

自己几乎没有额外新增一些常用功能!

HashSet集合的底层原理

前置知识:哈希值

  • 哈希值

就是一个int类型的数值Java中每个对象都有一个哈希值

Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值。

public int hashCode();返回对象的哈希码值。

对象哈希值的特点
    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
    • 不同的对象,他们的哈希值一般不相同,但也有可能会相同(哈希碰撞)
  • HashSet集合的底层原理
    • 基于哈希表实现
    • 哈希表是一种增删改查数据,性能都较好的数据结构。

JDK8之前HashSet集合的底层原理,基于哈希表:数组+链表

  1. 创建一个默认长度为16的数组,默认加载因子为0.75,数组名为table
  2. 使用元素的哈希值数组的长度求余计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果不为null,表示有元素,则调用equals方法比较

        相等,则不存,不相等,则存入数组

        JDK8之前,新元素存入数组,占老元素位置,老元素挂下面

        JDK8开始之后,新元素直接挂在老元素下面

哈希表是一种增删改查性能都较好的结构。

  1. 如果数组快占满了,会出什么问题?该咋办?

链表会过长,导致查询性能降低----------扩容(扩容成原来数组的2倍,再把原数组的内容重新转移到新数组中去)

JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树

JDK8开始HashSet集合的底层原理,基于哈希表:数组+链表+红黑树

了解一下数据结构(树)

普通二叉树(实际业务中基本不用)

:每一个节点的子节点数量

二叉查找树(二叉排序树)

规则: 小的存左边

            大的存右边

            一样的不存

二叉查找树存在的问题:

当数据已经是排好序的,导致查询的性能与单链表一样,查询速度变慢!

变成平衡二叉树

在满足查找二叉树的大小规则下,让树尽可能矮小,以此提高查数据的性能。

红黑树,就是可以自平衡的二叉树

深入理解HashSet集合去重复的机制。

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

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

结论:如果希望Set集合认为2个内容一样的对象是重复的,必须重写对象的hashCode()和equals()方法

LinkedHashSet集合

依然是基于哈希表(数组、链表、红黑树)实现的。

但是,它的每个元素都额外多了一个双链表的机制记录它前后元素的位置

TreeSet集合

  • 特点:不重复、无索引、可排序(默认升序排序,按照元素的大小,由小到大排序)
  • 底层是基于红黑树实现的排序。

注意

  • 对于数值类型:Integer、Double,默认按照数值本身的大小进行升序排序。
  • 对于字符串类型:默认按照首字符的编号升序排序。
  • 对于自定义类型如Student对象,TreeSet默认是无法直接排序的

自定义排序规则

TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。

方式一

  • 让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。

方式二

  • 通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。)

public TreeSet(Comparator comparator)

两种方式中,关于返回值的规则:

  • 如果认为第一个元素 > 第二个元素 返回正整数即可。
  • 如果认为第一个元素 < 第二个元素返回负整数即可。
  • 如果认为第一个元素 = 第二个元素返回0即可,此时TreeSet集合只会保留一个元素,认为两者重复。

注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。

总结:

  1. 如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据
  • 用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
  1. 如果希望记住元素的添加顺序,且增删首尾数据的情况较多
  • 用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
  1. 如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快
  • 用HashSet集合(无序、不重复、无索引),底层基于哈希表实现的。(常用)
  1. 如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
  • 用LinkedHashSet集合(有序、不重复、无索引),底层基于哈希表和双链表。
  1. 如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
  • 用TreeSet集合,基于红黑树实现。

注意事项:集合的并发修改异常问题

集合的并发修改异常

  • 使用迭代器遍历集合时,又同时删除集合中的数据,程序就会出现并发修改异常的错误。
  • 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误

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

  • 使用迭代器遍历结合,但用迭代器自己的删除方法删除数据即可
  • 如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i--操作

Collection的其他相关知识

前置知识:可变参数

可变参数

  • 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型...参数名称;
可变参数的特点和好处
  • 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。
  • 好处:常常用来灵活的接收数据。
可变参数的注意事项:
  • 可变参数在方法内部就是一个数组。
  • 一个形参列表中可变参数只能有一个
  • 可变参数必须放在形参列表的最后面

注意事项1:一个形参列表中,只能有一个可变参数。

注意事项2:可变参数必须放在形参列表的最后面

Collections

  • 是一个用来操作集合的工具类

Collections提供的常用静态方法

Collections只能支持对List集合进排序

排序方式1:

方法名称

说明

public static void sort(List list)

对List集合中元素按照默认规则排序

注意:本方法可以对自定类型的List集合排序,但自定义类型必须实现了Comparable接口,指定了比较规则才可以。

排序方式2:

方法名称

说明

public static void sort(List list,Comparator c)

对List集合中元素,按照比较器对象指定的规则进行排序

Map集合

概述

认识Map集合

  • Map集合称为双列集合,格式:{key1=value1, key2=value2, key3=value3, ...},一次需要存一对数据作为一个元素。
  • Map集合的每个元素“key=value” 称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”
  • Map集合的所有建是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值

Map集合在什么业务场景下使用

需要存储一一对应的数据时,就可以考虑使用Map集合来做

Map集合体系的特点

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

HashMap(由键决定特点):无序、不重复、无索引;(用的最多

LinkedHashMap(由键决定特点):由键决定的特点:有序、不重复、无索引。

TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。

常用方法

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

  • Map是双列集合的祖宗,它的功能是全部双列集合都可以继承过来使用的。

遍历方式

Map集合的遍历方式

  1. 键找值:先获取Map集合全部的键,再通过遍历键来找值

  1. 键值对:把“键值对”看成一个整体进行遍历(难度较大)

  1. Lambda:JDK1.8开始之后的新技术(非常的简单)

HashMap

HashMap集合的底层原理

HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。

实际上:原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据而已。

public HashSet() {

        map = new HashMap<>();

}

哈希表

  • JDK8之前,哈希表 = 数组+链表
  • JDK8开始,哈希表 = 数组+链表+红黑树
  • 哈希表是一种增删改查数据,性能都较好的数据结构。

HashMap底层是基于哈希表实现的

  • HashMap集合是一种增删改查数据,性能都较好的集合
  • 但是它是无序,不能重复,没有索引支持的(由键决定特点)
  • HashMap的键依赖hashCode方法和equals方法保证键的唯一
  • 如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。

LinkedHashMap

LinkedHashMap集合的原理

底层数据结构依然是基于哈希表实现的,只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。

实际上:原来学习的LinkedHashSet集合的底层原理就是LinkedHashMap。

TreeMap

  • 特点:不重复、无索引、可排序(按照键的大小默认升序排序,只能对键排序
  • 原理:TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。

TreeMap集合同样也支持两种方式来指定排序规则

  • 让类实现Comparable接口,重写比较规则。
  • TreeMap集合有一个有参构造器,支持创建Comparator比较器对象,以便用来指定比较规则。

补充知识:集合的嵌套

指的是集合中的元素又是一个集合

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的女IT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值