集合的概念
Collection接口
List接口与实现类
泛型和工具类
Set接口与实现类
Map接口与实现类
1 集合
一、概念
对象的容器,定义了对对象操作的常用方法。类似数组的功能。
二、与数组的区别
(1)数组长度固定,集合长度不固定
(2)数组可以存储基本类型和引用类型,集合只能存储引用类型
三、集合所在位置
java.util.*;
2 Collection体系
3 Collection父接口
一、特点
部分有序、部分无序;部分可重复、部分不可重复。
二、方法
1 | boolean add(Object obj) // 添加一个对象 |
4 Collection使用(1)
一、注意
由于没有下标,所以Collection中的元素遍历需要用foreach
或者迭代器Iterator
1 | package collection; |
另外,在迭代过程中,应使用迭代器的删除方法而不应使用collection的删除方法。
5 Collection使用(2)
1 | package collection; |
1 | package collection; |
6 List接口
一、方法
1 | void add(int index, Object o) // 在index位置插入对象o |
7 List使用(1)
一、ListIterator
与Iterator
区别
(1)ListIterator
可以任意方向遍历,Iterator
只能向后遍历
(2)ListIterator
可以添加和修改元素,Iterator
只能删除元素
1 | /* |
8 List使用(2)
1 | /* |
9 List 实现类
一、ArrayList
【重点】
(1)数组结构实现,查询快、增删慢
(2)jdk1.2版本加入,运行效率快、线程不安全
二、Vector
(1)数组结构实现,查询快、增删慢
(2)jdk1.0版本,运行效率慢、线程安全
三、LinkedList
(1)链表结构实现,增删快、查询慢
10 ArrayList使用
1 | /* |
一、重写equals
如上代码中删除元素的部分,由于会心创建对象,所以实际上并不会删除原有的元素,因为remove
会通过Object
的equals
方法,比较当前元素和集合中元素地址是否一样,一样就删除。如果想要通过这种方式删除元素,则需要重写Student
类中的equals
方法。
- 如何重写?
直接输入方法名称即可看到提示
一直next即可,自动填充重写的函数代码
11 ArrayList源码分析
一、变量
(1)默认容量大小10
1 | /** |
注意:如果没有向集合中添加任何元素,容量为0,源码的体现如下;添加任意元素后,容量为10,参考add源码部分
1 | /** |
(2)存放元素的数组
1 | /** |
(3)实际的元素个数
1 | /** |
二、方法
(1)add()
添加元素,是如何添加的?
1 | /** |
size初始为0,然后再看看ensureCapacityInternal()
1 | private static int calculateCapacity(Object[] elementData, int minCapacity) { |
三、ArrayList扩容
元素加满后,1.5倍扩容
12 Vector使用
https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Vector.html
1 | import java.util.Enumeration; |
13 LinkedList使用
1 | import collection.Student; |
14 LinkedList源码分析
(1)add
方法
1 | /** |
主要通过linLast
实现,所以看看linkLast
,就是在尾部添加元素
1 | /** |
15 ArrayList与LinkedList区别
本质就是数组和链表的区别。
数组:必须开辟连续空间,查询快,增删慢。
链表:不用开辟连续空间,查询慢,增删快。
16 泛型概述
有点类似于C++的模板
一、概念
Java泛型是JDK1.5中引入的新特性,其本质是参数化类型,把类型作为参数传递。
二、常见形式
泛型类、泛型接口、泛型方法。
三、语法
<T,...>
T表示类型占位符,表示一种引用类型
四、好处
(1)提高代码重用性
(2)防止类型转换异常,提高代码安全性
17 泛型类
一、编写泛型类
1 | /** |
二、注意
使用泛型类时,可以创建变量,但不能进行实例化。
原因:T
代表一种数据类型,但这种类型是不确定的,那它的构造方法就不一定能用,比如万一它的构造方法是私有的。
三、使用泛型类
1 | public class TestGeneric { |
18 泛型接口
一、new一个interface
新家接口文件的方法
二、编写接口
1 | /** |
三、注意
不能创建泛型静态常量,因为静态常量要求声明时进行初始化,但是又不知道T的类型,因此无法给它初始化。
四、接口的使用
A. 实现接口时明确类型
(1)说明
由于接口是不能实例化的,所以我们要添加一个实现类。在写实现类的时候给明确的引用类型。然后实现即可。
(2)编写
1 | public class MyInterfaceImpl implements MyInterface<String> { |
(3)使用接口
1 | MyInterfaceImpl impl = new MyInterfaceImpl(); |
B. 在实现时不确定的类型
(1)编写
1 | public class MyInterfaceImpl2<T> implements MyInterface<T> { |
(2)使用
1 | MyInterfaceImpl2<Integer> impl2 = new MyInterfaceImpl2<>(); |
19 泛型方法
参考《Java核心技术卷I》P313
泛型方法可以定义在普通类中,也可以定义在泛型类中。当调用一个泛型方法时,在方法名前的尖括号中放入具体的类型。
注意:大多数情况,不标记具体类型,编译器也可以做出正确的推断,但是最好还是带上,避免不必要的麻烦。(1)定义一个带有类型参数的简单泛型方法
1 | /** |
(2)使用
1 | MyGenericMethod myGenericMethod = new MyGenericMethod(); |
20 泛型集合
一、概念
参数化类型、类型安全的集合,强制集合元素的类型必须一致。
二、 特点
(1)编译时即可检查,而非运行时抛出异常
(2)访问时,不必类型转换(拆箱)
(3)不同泛型之间引用不能相互赋值,泛型不存在多态
三、关于类型转换的问题说明
样例程序如下
1 | import java.util.ArrayList; |
在上述代码中,虽然我们往arrayList中存入两种类型的数据,但是在遍历时因为用的Object,所以一切正常。但是如果遍历时我们定义一个String临时变量存放元素数据,就会产生异常了。
泛型集合就是可以解决这种问题。
四、泛型集合在创建集合对象时指定类型
在创建集合对象时指定类型,比如这里给的String,如果我们添加非String类型的元素就会报错了。
另外,也可以注意到,我们在遍历时可以直接用具体类型String而不再是Object,当然用Object遍历也是可以的。
1 | import java.util.ArrayList; |
21 Set集合
Set方法和Collection一模一样
22 Set接口使用
一、HashSet
(1)基于HashCode实现元素不重复
(2)当存入元素的HashCode相同,会调用equals确认,若结果为true,则拒绝后者存入
二、TreeSet(红黑树结构)
(1)基于排列顺序实现元素不重复
(2)实现了SortedSet
接口,对集合元素自动排序
(3)元素对象的类型必须实现Comparable
接口,指定排序规则
(4)通过CompareTo
方法确定是否为重复元素
三、使用示例
1 | import java.util.HashSet; |
23 HashSet实现类的使用(1)
1 | import java.util.HashSet; |
24 HashSet实现类的使用(2)
一、Person类
1 | public class Person { |
二、HashSet使用
1 | /** |
一、注意
这里的persons.add(new Person("人2", 29));
是可以成功添加元素的。因为这里new出来的对象和p2的地址是不一样的,如果添加p2就添加不进去。如果想实现名字和年龄相同时不允许加入,则需要重写hashCode()
。这里需要了解HashSet的存储方式。
25 HashSet存储方式
一、存储结构
Hash表(数组+链表+红黑树)
二、存储过程
(1)根据hashcode,计算保存的位置。如果此位置为空,直接保存;不为空,则(2)
(2)再执行equals,如果equals为true,则认为重复;否则,形成链表
三、重写hashCode()
在Person类中,重写hashCode
,方法同前文提到的重写equals
自动生成,无需修改。
26 HashSet补充
一、重写的hashCode
中,这个31是什么?
(1)31是一个质数,用质数做运算,可以尽可能减少散列冲突。(也就是说通过hashCode计算出来的位置尽量不一样)
(2)31*i=(i<<5)-i
,乘法操作可以换成位运算,提高执行的效率。
27 TreeSet使用
一、String类型
1 | import java.util.Iterator; |
二、普通类
当存储的是Person类对象时。
1 | import java.util.TreeSet; |
上述代码就会报错
原因是:元素对象的类型必须实现Comparable
接口,指定排序规则。否则程序无法确定需要比较的是什么。
因此需要改一下Person类。
然后根据提示,实现CompareTo
方法
添加逻辑,完善后的CompareTo
方法如下:
1 |
|
使用
1 | /* |
28 Comparator接口
实现定制比较
1 | /* |
29 TreeSet案例
使用TreeSet
实现字符串按长度排序。(首先需要定制Comparable
接口)
1 | import java.util.Comparator; |
30 Map集合概述
一、特点
(1)存键值对
(2)键值均无序无下标,键不允许重复,值允许重复。
31 Map接口使用
1 | import java.util.HashMap; |
32 HashMap使用(1)
jdk1.2后加入,线程不安全,运行效率快,允许null作为键或值
HashMap (Java SE 11 & JDK 11 ) (oracle.com)
构造函数这里的加载因子0.75是指:比如容量为100时,当元素个数达到75就扩容。
一、案例
1 | import java.util.HashMap; |
1 | import java.util.Objects; |
33 Hashtable
Jdk1.0版本,线程安全,运行效率慢,不允许使用null作为键值
Properties
Hashtable的子类,要求键值均为String。通常用于配置文件的读取
34 TreeMap
实现了SortedMap接口(是Map的子接口),可以对key自动排序
1 | package chapter12_4; |
1 | package chapter12_4; |