集合框架
概念
集合与数组类似, 是⼀个数据容器, ⽤来存储引⽤数据类型的数据。 在Java中, 集合不是泛指某⼀个类, ⽽是若⼲个类组成的数据结构的实现。
Java的集合类是 java.util 包中的重要内容,它允许以各种⽅式将元素分组,并定义了各种使这些元素更容易操作的⽅法。
Java集合类是Java将⼀些基本的和使⽤频率极⾼的基础类进⾏封装和增强后再以⼀个类的形式提供。
Java集合类是可以往⾥⾯保存多个对象的类,存放的是对象,不同的集合类有不同的功能和特点,适合不同的场合,⽤以解决⼀些实际问题。
集合的特点
- 集合类这种框架是⾼性能的。对基本类集(动态数组,链接表,树和散列表)的实现是⾼效率的。
- 集合类允许不同类型的集合以相同的⽅式和⾼度互操作⽅式⼯作
- 集合类容易扩展和修改,程序员可以很容易地稍加改造就能满⾜⾃⼰的数据结构需求
集合和数组的区别
-
存储的数据类型
- 数组中可以存储基本数据类型的数据, 也可以存储引⽤数据类型的数据
- 集合中只能存储引⽤数据类型的数据, 基本数据类型的数据需要进⾏装箱,才能存⼊集合中。
-
⻓度
- 数组是定⻓的容器, ⼀旦实例化完成, 数组的⻓度不能发⽣改变。 即数组不能动态添加、删除元素。
- 集合是变⻓的容器, ⻓度可以发⽣改变。 即集合中可以随时添加、删除元素。
-
元素操作
- 数组只能通过下标进⾏元素的访问, 如果需要其他的操作, 例如排序, 需要⾃⼰写算法实现。
- 集合中封装了若⼲对元素进⾏操作的⽅法, 直接使⽤集合, ⽐较⽅便。
使⽤集合的好处
-
降低编程难度:
在编程中会经常需要链表、向量等集合类,如果⾃⼰动⼿写代码实现这些类,需要花费较多的时间和精⼒。调⽤Java中提供的这些接⼝和类,可以很容易的处理数据。 -
提升程序的运⾏速度和质量:
Java提供的集合类具有较⾼的质量,运⾏时速度也较快。使⽤这些集合类提供的数据结构,程序员可以从“重复造轮⼦”中解脱出来,将精⼒专注于提升程序的质量和性能。 -
⽆需再学习新的APl:
借助泛型,只要了解了这些类的使⽤⽅法,就可以将它们应⽤到很多数据类型中。如果知道了LinkedList的使⽤⽅法,也会知道LinkedList怎么⽤,则⽆需为每⼀种数据类型学习不同的API。 -
增加代码重⽤性:
也是借助泛型,就算对集合类中的元素类型进⾏了修改,集合类相关的代码也⼏乎不⽤修改。
Java中的集合框架图
Java中的集合,⼤致分为两类。分别是 Collection 集合和 Map 集合。
其中,Collection是单列集合的顶级接⼝,Map接⼝是双列集合的顶级接⼝。
Collection集合
存储特点
Collection接⼝是单列集合的顶级接⼝。 在这种集合中存储的数据, 只占⼀列。 所有的元素直接存储于各种数据结构中。
Collection集合中, 没有下标的概念。
Collection API
List集合
存储特点
List集合是单列集合,是Collection接⼝的⼦接⼝。Collection接⼝中所有的⽅法,这⾥都有。同时,这个集合⽐Collection集合,多个若⼲⽅法。
在List接⼝中,是有下标的概念的。多出来的这些⽅法,基本也都是围绕着下标操作的。
List与Set对⽐
- List:存储的数据是有序的(元素的存储顺序与添加元素的顺序⼀致),可以重复的.
- Set:存储的数据是⽆序的,不可以重复
List API
List集合排序
在List接⼝中,提供了⼀个排序的⽅法 sort
⽅法原型
default void sort(Comparator<? super E> c)
⽅法逻辑
这是⼀个系统封装好的⼀个⽤来做排序的⽅法,由于集合中只能存储引⽤数据类型的数据,因此,需要明确两个元素的⼤⼩关系。参数Comparator是⼀个对象⼤⼩⽐较的接⼝。在这个接⼝的⽅法中,有两个参数,分别表示参与⽐较的两个对象。返回值是int类型,不看具体的值,只看正负。
List集合遍历
由于List接⼝是继承⾃Collection接⼝的,因此在Collection部分的三种遍历⽅式,都可以⽤来遍历List集合。同时,在List集合中,还添加了两种⽤来遍历集合的其他⽅
式。
- 下标遍历
类似于数组的下标遍历。 遍历集合中的所有的下标, 依次获取指定下标位的元素。
- 列表迭代器
这种⽅式, 类似于迭代器。 在List集合中, 有⼀个⽅法 listIterator() , 可以获取到⼀个 ListIterator 接⼝的引⽤。 ⽽ ListIterator 是 Iterator 的⼦接⼝。 因此在保留了传统的迭代器的迭代⽅法的基础上, 还添加了若⼲个其他的⽅法。ListIterator 中新增了hasPrevious()和previous()⽅法
当我们使⽤hasnext()和next()⽅法实现从左到右遍历后,可以继续使⽤使⽤hasPrevious()和previous()⽅法从右到左遍历.
使⽤ListIterator, 在遍历集合中的元素的同时, 可以向集合中添加元素、删除元素、修改元素。
但是, 这⾥对集合中的元素操作, 并不是使⽤ List 接⼝中的⽅法, ⽽是⽤ListIterator 接⼝中的⽅法完成。
ArrayList与LinkedList对⽐
-
相同点
- 都是List集合的常⽤的实现类。
- 对集合中的元素操作的⽅法基本⼀致。
- 都是线程不安全的
-
不同点
- ArrayList底层实现是数组, 使⽤数组这种数据结构进⾏数据的存储。
- LinkedList底层实现是双链表, 使⽤双链表这种数据结构进⾏数据的存储。
数组与链表结果特点⽐较:
- 数组实现功能时查找快,添加删除慢
- 链表查找慢,添加删除快
ArrayList与LinkedList的使⽤场景:
- 如果对集合中的元素, 增删操作不怎么频繁, 查询操作⽐较频繁。 使⽤ArrayList。
- 如果对集合中的元素, 增删操作⽐较频繁, 查询操作不怎么频繁。 使⽤LinkedList。
集合对⾃定义对象的存储
- 类分成系统类和⾃定义类
- 系统类往往已经重写了tostring(),equals(),hashcode()等⽅法
- ⾃定义类,为了实现我们的功能,往往我们需要重写⽗类的常⽤⽅法,⽐如:tostring(),equals(),hashcode()
LinkedList
对于LinkedList的特有⽅法对⽐:
jdk1.6以前的删除获取⽅法,拿不到元素报异常
jdk1.6以后的删除获取⽅法,拿不到元素返回null
//获取的对象不存在会发⽣异常 NoSuchElementException
//E getFirst()
//E getLast()
//System.out.println(linkedList.getFirst());
//删除的对象不存在会发⽣异常
//E removeFirst()
//E removeLast()
//从jdk1.6开始出现以下⽅法
//offerFirst()
//offerLast()
//获取的对象不存在会返回null
//peekFirst()
//peekLast()
System.out.println(linkedList.peekFirst());
//删除的对象不存在会返回null
//pollFirst()
//pollLast()
泛型
泛型在类中的使⽤
定义: 在类名的后⾯, 紧跟上⼀对尖括号。
class Animal <T> {}
class Dog <T, M> {}
泛型类的使用: 声明引⽤、实例化对象、被继承。
泛型类的特点
- 在类中定义的泛型, 虽然还不明确是什么类型, 但是在当前类中是可以使⽤的。
- 在使⽤到这个类的时候, 必须要指定泛型的类型。 如果不指定, 默认是Object
- 泛型, 只能在当前的类中使⽤, 不能在其他的类中使⽤, 包括⼦类。
泛型在接口中的使用
泛型接⼝的定义: 在接⼝名字的后⾯, 添加上⼀对尖括号。 在尖括号⾥⾯定义泛
型。
interface MyInterface<T> {}
interface MyInterface<T, M> {}
泛型接⼝的使⽤: 实现类实现接⼝、 使⽤接⼝访问接⼝中的静态成员、 被继承。
// 1. 实现类实现接⼝
class MyInterface1Impl implements MyInterface1<Person> {}
// 2. 被继承
interface SubMyInterface extends MyInterface1<String> {}
泛型接⼝的特点
-
在接⼝中定义的泛型, 虽然还不明确是什么类型, 但是在当前接⼝中是可以使⽤的。
-
在使⽤到这个接⼝的时候, 必须要指定泛型的类型。 如果不指定, 默认是Object。
-
泛型, 只能在当前的接⼝中使⽤, 不能在其他的接⼝中使⽤, 包括⼦接⼝。
⼦类使⽤接⼝泛型
这⾥有两种情况:
第⼀种:⼦类与接⼝泛型⼀致,接⼝可以直接使⽤泛型类型
第⼆种:接⼝使⽤泛型,⼦类不⽤,在实现的接⼝位置必须指定⼀个具体的数据类型
泛型在⽅法中的使⽤
定义: 泛型⽅法中, 在定义⽅法时,在返回值前⾯通过定义泛型。
public static <T> void test(T t) {
}
- 在定义处的<>⽤来定义泛型
- 在调⽤处的<>⽤来使⽤泛型
泛型⽅法的分类
- 第⼀:⽅法上的泛型与类上的⼀致
- 第⼆:⽅法上独⽴使⽤泛型
- 在⽅法中定义的泛型, 虽然还不明确是什么类型, 但是在当前⽅法中是可以使用的。
- 泛型⽅法, 在使⽤的时候, 不能跟类、接⼝似的, ⼿动的设置类型。 泛型⽅法中, 泛型的设置, 在参数中体现。
- 泛型⽅法, ⼀定需要是有参的。 参数列表中, 必须有泛型类型。
- 泛型⽅法中的泛型的设置, 是通过在调⽤⽅法的时候, 实参的类型推导出
来的。 - 泛型, 只能在当前的⽅法中使⽤, 不能在其他的⽅法中使⽤。
⽅法上独⽴使⽤泛型
-
在⽅法中定义的泛型, 虽然还不明确是什么类型, 但是在当前⽅法中是可以使用的。
-
泛型⽅法, 在使⽤的时候, 不能跟类、接⼝似的, ⼿动的设置类型。 泛型⽅法中, 泛型的设置, 在参数中体现。
-
泛型⽅法, ⼀定需要是有参的。 参数列表中, 必须有泛型类型。
-
泛型⽅法中的泛型的设置, 是通过在调⽤⽅法的时候, 实参的类型推导出
来的。 -
泛型, 只能在当前的⽅法中使⽤, 不能在其他的⽅法中使⽤。
-
第三:静态⽅法使⽤泛型