集合
使用简单
进阶:
底层机制
源码
什么情况下用什么集合
数组缺点:
创建长度必须指定
必须保持同一类型
扩容/删除麻烦
集合:
动态保存任意多个对象object
提供一系列简单操作对象的方法
添加/删除简单
集合的选择:
一组对象:Collection接口
允许重复List
增删多LinkedList
改查多ArrayList
不允许重复Set
无序HashSet
有序LinkedHashSet
排序TreeSet
一组键值对:Map接口
键无序HashMap
键有序LinkedHashMap
键排序TreeMap
读取文件Properties
Collection 单列集合,是接口,
集合遍历方式:
迭代器:
继承了Iterable接口,迭代器
所有实现Collection接口的集合类都有iterator()方法,返回一个iterator接口对象/迭代器
iterator仅用于集合遍历,并不存放集合对象 iterator迭代器的hashNext方法与next方法
快捷键itit 显示所有快捷键:ctrl+j
注意:退出迭代器循环后,迭代器指向集合最后一个元素,如果要再次遍历,要重置迭代器(重新用一次iterator()方法)
增强for循环
【只能用于遍历】数组(底层是普通for)与集合(底层是迭代器)
快捷键:I/iter
存储
可以存储object(不加泛型的情况下)
常用方法:
add 参数是对象,与for经常配合
remove 参数是下标/指定对象
contains 返回boolean
size
isEmpty
clear 清空
addAll 参数是集合
containsAll判断多个元素是否都存在,参数可以是集合
removeAll 参数可以是集合
分类:有很多,不止下面2个
List接口,【有序】
遍历:
迭代器
增强for循环
普通for+get方法
存储:
可重复
有序,支持索引
常用方法:
get方法,参数是索引
remove方法,参数是索引/对象
add 第一个参数是索引,第二个是对象,表示从哪里开始添加
addAll 第一个参数是索引,第二个是对象,表示从哪里开始添加
set 第一个参数是索引,第二个是对象,表示替换 与get配合,可以用于集合元素的交换(不用temp了)
indexOf 参数是对象,返回索引
lastIndexOf 参数是对象,返回索引
subList 参数是2索引,返回小集合,左闭右开
sort方法 参数是无,或比较器
分类:有很多,不止下面3个
ArrayList类 jdk1.2
源码分析:
toString 继承的父类AbstractCollection类重写了toString,打印的是集合内容
底层是transient object数组实现,创建对象,无参容量默认0(jdk7默认10)
可以放所有元素(基本类型会先装箱),包括null,而且可以是多个
transient表示瞬间,短暂的,被修饰的 表示该属性不会被序列化
添加元素add方法,判断是否需要扩容,需要调用grow方法,不需要直接添加
无参构造器,需要扩容,第一次扩容默认10,再次扩容,是1.5倍(10的)
指定容量构造器,第一次指定容量,再次扩容,是1.5倍(指定容量的)jdk不一样,就不一样
扩容是Arrays.copyOf方法
每次add都要判断是否需要扩容(影响效率)
debug可以看到扩容,但是注意:idea的debug调用的是toString,是简化的,可能看不到扩容
看完整数据,要设置-settings-builddebugger-data views-java-
Enable alternative view for。。与Hide null。。取消勾选
arraylist基本等同Vector,但是线程不安全,执行效率高,多线程不建议使用,
方法没有同步关键字,
有modCount,记录集合修改次数,多线程修改抛异常
Vector类 jdk1.0
源码分析:
底层也是对象数组;线程安全,效率不高
无参就是默认10,不够,2倍扩容
LinkedList类
源码分析:
底层是双向链表,增删效率高
transient的size
实现了双向链表与双端队列的特点(first,last,内部类final Node(prev,item,next))
无参构造/以集合作为参数,链表不能指定长度
可以添加任意元素,元素可以重复,包括null,而且可以是多个
线程不安全,效率高
注意:remove==removeFirst默认删除第一个
选择:
改查用arraylist 80%
增删用linkedlist
业务模块不同,集合也可以不同
Set接口,【无序】
遍历:
迭代器
增强for
不能用索引遍历
存储:
无序,没有索引,虽然无序,但是取出顺序一定的,按地址哈希值取
不允许重复,最多一个null
add源码看如何确定是否重复:
得到哈希值-》哈希值运算放入数组-》已经有了,equals方法判断
-》相同,不添加;不相同,链表形式/红黑树添加
常用方法:
add 添加多次不会报错,但是还是只有一个
remove 参数是对象
分类:有很多,不止下面2个
HashSet类
源码分析:
存储的是对象地址,对象存入后发生改变,其哈希值改变,存入位置自动变
实现了Set接口,实际上是hashMap(hashSet构造器),hashMap底层是数组+链表/红黑树
不允许添加重复元素
hashMap底层
add源码看如何确定是否重复:put方法,key变化,value一直是object,用于占位(Map-》Set)
得到哈希值-》哈希值运算得到索引值,放入数组-》数组已经有了,equals方法判断
-》相同,不添加;不相同,链表形式/红黑树添加
jdk8,一个链表满8(不是超过),且数组长度大于64(默认16),整体变为红黑树
满8,但是数组长度(table)没有大于64,会先将table数组扩容
add底层:
转put方法,执行hash方法,。得到key的哈希值,转putVal
看table表是否存在(是否是null)不存在,转realize方法
存在,计算下标值,判断数组的下标位置是否为null,是,放入;
不是,判断是不是同一对象/equals,是,返回;判断是不是红黑树;
判断在不在链表中,添加满8个,准备进行树化(不一定真正树化,可能是数组扩容)
数组扩容有加载因子0.75,16长度,到12就开始扩容了,每次扩容2倍
这个12包括加入在数组第一个位置/链表位置 都算size(不是只算数组的)
数组扩容(之前的不变,之后来的重新取模?)
linkedHashSet
源码分析:
底层是LinkedHashMap,数组+双向链表 是Entry,静态内部类,继承了Node(也是静态内部类)
用链表维护元素次序,使其有序(第一个加入的指针指向下一个加入的节点【即使是不同hashcode值】)
不允许添加重复元素
TreeSet类
无序(String默认字典序从小到达)
与HashSet一样/HashTable一样,
但是可以排序,使用构造器,传入一个比较器(用匿名内部类) 底层add方法会自动调用
是key,所以加重复的是加不进去,而不是value的替换
底层是HashTable,是Entry
去重机制:构造器传入的比较器comparator的compare方法去重,没有传入比较器,
用对象实现的comparable接口的compareTo去重(字符串String是比较的内容)
适用TreeSet的对象应该要实现这个接口
Map 双列集合,是接口
源码:
key不允许重复,哈希值和equals,可以为null(一个),常用String类作为key (放入重复是替换value操作)
value可以重复,可以为null(多个)
key可以找到value,
2者真正是封装到一个Node里(Node实现了Entry接口,所以也叫一个Entry)
为了方便程序员的遍历,还会创建 EntrySet的Set集合,该集合存放元素的类型Entry,而一个Entry对象就有k-v
EntrySet是Map的内部类,entrySet中定义的类型是Map.EntrySet,但是实际上存放的还是HashMap$Node
这是因为Node实现了Map.EntrySet接口
Map.EntrySet接口实现了getKey与getValue方法方便遍历(Set实现了Collection接口)
map.keySet获取set集合封装key
map.values获取value集合封装value
keySet与values也是map的内部类,实现了单例集合接口,方便遍历
常用方法:
put 参数是2对象
get 参数是key
remove参数是key
size
isEmpty
clear
containsKey
entrySet 存储Map.entry类型,其实完全可以换成Node
keySet
values
遍历:
keySet 增强for;迭代器
entrySet 增强for;迭代器
分类:
HashMap类 jdk1.2
源码:
无序(底层hash表与链表/红黑树)树化是jdk8才有的
继承父类的toString,打印内容
方法没有同步关键字,线程不安全,效率高
树结构删除不够多,就会剪枝=》重新转为链表
LinkedHashMap类
有序
HashTable类 jdk1.0
源码:
键值都不能为null(抛异常)
线程安全,效率不高
put相同键是替换
底层是Entry数组(静态内部类),初始大小为11,加载因子0.75 阈值就是8
操作与hashMap基本相同
Properties类
使用与hashtable类似,不能放null,也是替换
更多是从点Properties文件中加载数据到Properties对象,进行读取与修改
(点Properties文件通常作为配置文件,与IO流有关,一般保存用户名,密码,用于软编码)
TreeMap类
可以排序,底层是树,没有数组扩容一说
底层是Entry
重复会替换value
Collections工具类
静态方法操作Set,List,Map集合
排序:
reverse(List)反转
shuffle(List)随机排序
sort(List) 自然升序 (String就是字典序)
sort(List,Compator)
swap(List,int i,int j)交换i,j元素
查找,替换
max/min(Collection)返回自然顺序最大的/最小的(String就是字典序)
max/min(Collection,compator)
frequency(Collection,object)返回出现次数
copy(List dest,List src) 复制集合(注意参数位置)
replace(List,object oldVal,object newVal)使用新值替换所有旧值
泛型 jdk5才有 编译期会有擦除机制
引入:
传统方法不能对加入到内的集合进行约束,是不安全的
传统方法遍历时,需要进行类型转换,如果集合的数据量较大,对于效率有影响
主体:
泛型就是一种程序员指定的一种数据类型(可以是任意引用类型),主要功能之一是限制集合类型
可以在类声明时,表示属性类型,方法参数类型,返回值类型(类模板) 编译时确定泛型 类中getClass方法可以得到
可以有多个
iterator接口返回与泛型有关的迭代器(源码有关)
可以传入泛型子类型
不写泛型,其实也有泛型E,只不过默认是object
自定义泛型
泛型类 可以有多个泛型
使用泛型的数组不能初始化(不能确定类型,不知道开辟多少空间)
静态变量/静态方法中不能使用类的泛型 (泛型是对象创建确定,static是类加载时期)(这里的方法是普通静态方法)
泛型类的类型,是在创建对象时确定的,没有指定,默认object
泛型接口 可以有多个泛型
静态属性/静态方法不能用接口的泛型
泛型接口,泛型的确定在继承接口/实现接口时确定(都可以)
没有指定,默认object
泛型方法 可以有多个泛型
泛型放修饰符后面, 方法参数/方法体内可以使用此类型,还可以使用类定义的泛型(定义就是用的,一个类型而已)
泛型没有在修饰符后面,不是泛型方法,只是方法使用了泛型而已(还要注意,这个泛型要是定义过的!!!)
(使用就要先定义)
可以定义在泛型类/普通类
泛型方法被调用时,确定类型 (正常传参,编译器会自动确定)
【注意】:
【泛型静态】方法可以使用泛型方法中定义的泛型/泛型通配符,
不能使用类/接口有但泛型方法没有定义的泛型
泛型方法内的泛型符号可以与类泛型一致,但只是符号一致,完全是独立的,方法中用的是方法的【方法独立?应该是静态方法独立】
(最好避免相同)
泛型继承与通配符
泛型不具备继承性 List