java基础笔记09-集合

本文详细介绍了Java集合框架中的数组、集合、List接口、ArrayList和LinkedList的区别,以及Set接口和HashSet的实现原理。讨论了动态扩容、迭代器的使用,并提到了序列化和反序列化的基本概念。还对比了ArrayList和LinkedList在不同操作场景下的性能差异,强调了选择集合类的重要性。最后,简要提及了HashSet的扩容机制和LinkedHashSet的特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  1. 数组的不足
  • 长度初始必须指定,一旦指定不能更改
  • 保存的必须为同一类型的元素
  • 使用数组增加元素的代码比较麻烦
  1. 集合好处:
  • 动态保存任意多个对象
  • 使用了一系列方便的操作对象的方法,add、remove、set、get
  • 添加删除元素的代码简洁
  1. 集合
  • 集合主要是两组,单列集合和双列集合
  • Collection有两个重要子接口,List和Set,它们的实现子类都是单列集合
  • Map接口的实现子类是双列集合,存放的K-V
    map结构图
    Collection结构图
  1. Collection接口实现类的特点
  • 可以存放多个元素,每个元素可以是Object
  • 有些Collection的实现类,可以存放重复元素,有些不可以
  • 有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)
  • Collection没有直接的实现子类,而是通过它的子接口Set和List来实现的
  1. Collection方法
  • add添加某个元素
  • remove删除某个元素
  • contains查找元素是否存在
  • size获取元素个数
  • isEmpty判断是否为空
  • clear清空列表
  • addAll添加多个元素
  • containsAll查找多个元素是否都存在
  • removeAll删除多个元素
  1. Collections接口遍历元素的方式1-使用Iterator
  • Iterator称为迭代器,主要用于遍历Collection集合中的元素
  • 所有实现了Collection接口的集合类都有一个iterator()方法,用于返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
  • Iterator仅用于遍历集合,本身并不存放对象
  1. Iterator的迭代原理
    Iterator iterator = coll.iterator(); // 得到一个集合的迭代器
    // 判断是否还有下一个元素
    while(iterator.hasNext()){
    iterator.next();// 作用1.下移,2.将下移以后集合位置上的元素返回
    }
  • 显示所有快捷键(win)ctrl+j (mac)command+j
  • 退出while循环后迭代器指向最后的元素,如果想再次遍历,需要重置迭代器
    iterator = col.iterator();
  1. Collections接口遍历元素的方式2-增强for
  • 可以用在集合和数组上
for (Object obj : collection) {
	System.out.println(obj);
}
  • 底层还是迭代器,可以理解成简化版迭代器
  1. List接口(Collection的子接口)
  • List集合类中元素有序(添加顺序和取出顺序一致),且可重复
  • List集合中的每个元素都有其对应的顺序索引,即List支持索引
  • add(index, object)在index位置插入object
  • addAll(index, collection)在index位置插入collection的所有元素
  • get(index)获取指定index位置的元素
  • indexOf(object)返回object在集合中首次出现的位置
  • lastIndexOf(object)返回object在集合中最后一次出现的位置
  • remove(index)移除指定位置的元素
  • set(index, object)设置指定index位置的元素为object,相当于替换
  • subList(fromIndex, toIndex)返回fromIndex <= 子集合 < toIndex位置的子集合
  • 遍历方式:1.iterator 2. 增强for 3. 普通for
  1. ArrayList注意事项
  • 可以加入null
  • 底层是由数组来存储的
  • ArrayList基本等同于Vector,除了是线程不安全的,执行效率高(多线程情况下不建议使用ArrayList)
  1. ArrayList底层结构和源码分析
  • ArrayList中维护了一个Object类型的数组elementData transient Object[] elementData
  • transient表示瞬间、短暂的,表示该属性不会被序列化
  • 创建ArrayList对象时,如果使用的是无参构造器,则初始elementData为0;第一次添加则扩容elementData为10,如需再次扩容,扩容elementData为1.5倍。
  • 如果使用的是指定大小的构造器,则初始elementData容量为指定大小;如需扩容,则直接扩容elementData为1.5倍
  • 扩容调用的是Arrays.copyOf()
  1. Vector底层结构和源码分析
  • 底层是一个对象数组,object[] elementData
  • 是线程同步,操作方法基本都带有synchronized
  • 需要线程同步安全时建议使用VectorArrayList和Vector对比
  1. LinkedList
  • 底层实现了双向链表和双端队列特点
  • 可以添加任意元素,包括null
  • 线程不安全,没有实现同步
  • 底层维护一个双向链表,first和last分别指向首节点和尾节点
  • 元素添加和删除不是通过数组完成的,相对来说效率较高
  • 模拟一个简单的双向链表
LinkedList linkedList = new LinkedList();
linkedList.add(100);
linkedList.add("hello");
linkedList.remove();
linkedList.getFirst();
  1. ArrayList vs LinkedList
    ArrayList和LinkedList
  • 改查操作较多,使用ArrayList
  • 增删操作较多,使用LinkedList
  • 一般来说在程序中,80%-90%都是查询,大部分情况下选择ArrayList
  • 根据业务灵活选择
  1. Set接口
  • 无序,添加和取出的顺序不一致,没有索引,添加的顺序和取出的顺序不一致,但是取出的顺序是固定的
  • 不允许有重复元素,最多包含一个null
  • 是Collection的子接口,常用方法和Collection一样
  • 遍历方式:1.使用迭代器 2. 增强for 3.不能使用索引的方式来获取
  1. HashSet实现类
  • 是set的实现类
  • 底层是HashMap
  • 可以存放null,但是只能有一个null
  • 无序,不保证取出顺序和添加顺序一致
  • 不能有重复元素/对象
  • add方法添加元素,返回boolean值,是否添加成功
Set set = HashSet();
set.add(new Dog("tom")); //t
set.add(new Dog("tom")); //t
set.add(new String("tom")); //t
set.add(new String("tom")); //f
  1. HashSet底层模拟(模拟HashMap)
  • HashSet底层是HashMap,HashMap底层是数组+单向链表+红黑树(链表满足一定量时且数组在某个范围之内,就会把链表进行树化)
// 模拟简单的数组+链表
// 创建一个数组,数组类型是Node[]
// 有些人把Node[] 称为表
// 考虑效率
Node[] table = new Node[16];
Node rose = new Node(null, "rose");
Node jack = new Node(rose, "jack");
Node john = new Node(jack, "john");
table[2] = john;
System.out.println(table);
  1. HashSet底层机制
  • HashSet底层是HashMap
  • 添加一个元素时先获取元素的Hash值,然后转成索引值。
  • 找到存储数据table表,看这个索引位置是否已经存放元素
  • 如果没有,直接加入
  • 如果有,调用equals比较,如果相同就放弃添加;如果不相同就添加到最后。
  • JDK8中,一条链表元素个数大于等于8(TREEIFY_THRESHOLD),并且table的大小大于等于64(MIN_TREEIFY_CAPACITY),就会进行树化。
  1. HashSet扩容机制
  • HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值是16*0.75 = 12
  • 如果table数组使用到了12,就会扩容到16 * 2 = 32,新的临界值就是32* 0.75 = 24,依此类推
  • Java8中,一条链表的 元素个数到8,且table大小到64,就会进行树化,否则仍然采用数组扩容机制。
  1. LinkedHashSet
  • 是HashSet子类
  • LinkedHashSet底层是LinkedHashMap,底层维护了一个数组+双向链表
  • LinkedHashSet根据元素的hashcode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
  • 不允许添加重复元素
  • 加入顺序和取出的顺序一致
  • 第一次添加时,table数组扩容到16,存放的结点类型是LinkedHashMap$Entry
  • 数组是HashMap$Node,存放的数据类型是LinkedHashMap$Entry
  • 每一个节点有before和after属性,这样可以形成双向链表
  1. 序列化和反序列化
    经常听到实现Serializable接口就可以进行序列化,那么序列化到底是什么?
    序列化:将数据结构or对象转为二进制字节流的过程
    反序列化:二进制字节流转为数据结构or对象的过程
  • 序列化一般在OSI七层协议的表示层,TCP/IP四层协议的应用层
  • JDK自带的序列化,只需要实现java.io.Serializable
  • SerialVersionUID用于版本控制。序列化时会和数据一起写入二进制序列。反序列时会检查是否和当前类的serialVersionUID一致,不一致会抛出InvalidClassException异常。推荐手动指定SerialVersionUID,否则编译器回动态生成默认的序列化号。
  • 很少使用JDK自带的序列化方式。常用的序列化协议:hessian、kyro、protostuff
  • kryo是专门针对java语言序列化方式且性能非常好。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值