Java基础知识章节梳理(十六)
文字数:约2000字 阅读时间:30分钟
此文章为学习总结类型,文字很多且皆为基础知识
一. 集合
1. 集合框架的由来
1) 每一个容器的数据结构(数据存储到的一种方式)不一样。
2) 不同的容器进行不断的向上抽取,最后形成了一个集合框架这个框架就是 Collection 接口。
3) 在 Collection 接口定义着集合框架中最最共性的内容。
2. Collection接口
Collecition是所有集合的顶层接口(所有集合的父类)
2.1 常用的子接口
2.1.1 List接口(常用的子类有)
1) 有序的,可以重复的
2) ArrayList类
3) LinkedList类
2.1.2 Set接口(常用的子类有)
1) 无序的,不可重复的
2) HashSet 类
3) LinkedHashSet 类
其中的共性,见下方的成员方法。
2.2 常见的成员方法
1) add(E e):将指定E类型的元素e添加到集合中,返回的是一个布尔类型的值
2) clear():删除此Collection中的所有元素(可选操作)
3) contains(Object o):如果此Collection包含指定的元素,则返回true(集合与元素o的比较)
4) equals(Object o) :比较此 collection 与指定对象是否相等。(通常用于两个集合对象的比较)
5) remove(Object o):从此Collection中移除指定元素的单个实例,如果存在的话(可选操作)
6) isEmpty() :如果此 collection 不包含元素,则返回 true
7) size():返回此Collection中的元素数
8) toArray():返回包含此Collection中所有元素的数组,返回值是一个Object类型的数组
3. 集合与数组的区别
1) 集合只能存放引用数据类型,不能存放基本数据类型,
如果想存放数据类型,需要使用基本数据类型的包装数据类型,
而数组可以存放任意数据类型
2) 集合创建对象的时候,可以不指定长度也可以不指定内容,而数组创建对象的时候必须制定长度或元素
3) 一个集合对象中可以存放多种数据类型,而数组中只能存放一种数据类型
4) 集合的长度是可变的,而数组的长度是固定的
4. 二者选用的规则
当需要一个容器既不能指定容器的内容,也无法确定容器的长度时,可以使用集合,否则建议使用数组效率相对较高
二. Iterator迭代器
1. 迭代器概述
1) 当从集合中获取元素的时候,有一个通用的流程,将这个通用的流程描述成了一个接口,这个接口就是Iterator
2) 通用流程:在取元素之前先要判断集合中有没有元素,
如果有,就把这个元素取出来,继续在判断,
如果还有就再取出出来。一直把集合中的所有元素全部取出。
这种取出方式专业术语称为迭代。
2. 使用方式
1) Collection 接口描述了一个抽象方法 iterator 方法,所有 Collection 子类都实现了这个方法
2) iterator():返回在此collection的元素上进行迭代的迭代器
返回值是:Iterator<E>(E表示集合中的数据类型)
3. Iterator接口两个常用方法
3.1 hasNext()方法
用来判断集合中是否有下一个元素可以迭代。如果返回 true,说明可以迭代。
3.2 next()方法
用来返回迭代的下一个元素,并把指针向后移动一位,若集合中已经没有元素了还继续使用将报NoSuchElementException 没有集合元素的错误。
为方便更好的了解,其中把iterator的源码截取过来并结合上面的步骤进行了解析
(备注:因为迭代器定义成了接口,因此为了方便使用者使用,ArrayList使用内部类的方式实现了其接口)
4. 增强for循环(迭代器的简化形式)
4.1 定义
1) 增强 for 循环是 JDK1.5 以后出来的一个高级 for 循环,专门用来遍历数组和集合的。
2) 它的内部原理其实是个 Iterator 迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作。
4.2 格式
for(元素的数据类型 变量名 : 被迭代的Collection 集合 or 数组){
//此时变量名代表被遍历到的数组或集合元素
}
4.3 增强 for 循环和老式的 for 循环的区别
1) 新 for 循环必须有被遍历的目标。目标只能是 Collection 或者是数组。
2) 遍历数组时,如果仅为遍历,可以使用增强 for ,不能对元素进行增删操作
3) 如果要对数组的元素进行操作,使用老式 for 循环可以通过角标操作。
5.注意事项
在使用迭代器遍历的过程中不能使用集合自身的方法对元素进行增删操作,否则会出现并发修改异常
三. 泛型
1. 概述
1) 泛型就是一个特殊的变量:保存的是引用数据类型
2) 泛型用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数传递。
2. 使用场景
一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为 Object 类型。
3. 定义格式
3.1 含有泛型的类
3.1.1定义格式
修饰符 class 类名<代表泛型的名字> { }
3.1.2 使用格式
例一
例如,ArrayList<String> list = new ArrayList<String>();
此时,变量 E 的值就是 String 类型
例二
例如,ArrayList<Integer> list = new ArrayList<Integer>();
此时,变量 E 的值就是 Integer 类型
3.1.3注意事项
泛型里不存在多态,即左右两边尖括号里的数据类型必须一致
(后面的尖括号可以不写数据类型)
3.2 含有泛型的方法
3.2.1 定义格式
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
3.2.2 使用格式
调用方法时,确定泛型的类型
例如
3.3 含有泛型的接口
接口中只有抽象方法的返回值和参数类型可以使用泛型
3.3.1 定义格式
修饰符 interface 接口名<代表泛型的变量> { }
3.3.2 由于接口不能直接创建对象,因此接口中的泛型值有两种赋值方式
1、定义类时确定泛型的类型(写死了)
2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型(定义子类时确定泛型)
4. 泛型通配符
1) 当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示
? extends E (代表只要是 E 类型的子类即可)(限定上限)
? super (E 代表只要是 E 类型的父类即可)(限定下限)
2) 一旦使用泛型的通配符后,只能使用 Object 类中的共性方法,集合中元素自身方法无法使用。
5. 使用泛型的好处
1) 将运行时期的 ClassCastException,转移到了编译时期变成了编译失败。
2) 避免了类型强转的麻烦以及强转时可能出现的异常
3) 意思就是 如果使用了泛型,在代码编译时期如果输入的不是泛型指定的数据类型的话编译会报错