Collection集合、List、泛型
自述
感觉自己java基础有些薄弱,打算从0开始学习,每天打卡并且记录自己学习的内容,有兴趣小伙伴评论q1一起进步!!!敬请关注小屋(cottage_123)谢谢支持,如要搬运请挂载链接,编写不易,感谢!
一、Collection集合
1.1 集合概述
集合:集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有什么区别呢?
* 数组的长度是固定的!可以存放任意类型的数据,只不过,我们在开发的时候经常将同一类型的数据放入一个数组!【它的操作非常麻烦的,没有方法处理】
Object[] array = new Object[5];
array[0] = true;
array[1] = 100;
array[2] = "Niubility";
* 集合的长度是可变的!也可以存放任意类型的数据!实际开发,集合里面一般也是放相同类型的数据!【它的操作非常方便的,提供了很多便捷的方法】
ArrayList list = new ArrayList();
list.add(true);
list.add(100);
list.add("Niubility");
1.2 集合体系结构
1.3 Collection 常用功能
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用
的一些方法,这些方法可用于操作所有的单列集合。
public boolean add(E e): // 把给定的对象添加到当前集合中 。【E如果指定了具体类型,那么就只能向集合中添加指定类型的数据,没有指定具体类型,就相当于Object】
public void clear(): // 清空集合中所有的元素。
public boolean remove(E e): // 把给定的对象在当前集合中删除。
public boolean contains(Object obj): // 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): // 判断当前集合是否为空。
public int size(): // 返回集合中元素的个数。
public Object[] toArray(): // 把集合中的元素,存储到数组中
代码演示:
public class Test02 {
public static void main(String[] args) {
// 创建Collection接口的子类对象
Collection coll = new ArrayList(); // 父接口的引用指向子类对象(多态)
// 这里只能调用coll里面的方法,不能调用子类独有的方法!
// public boolean add(E e): // 把给定的对象添加到当前集合中 。
coll.add("it");
coll.add("java");
coll.add("666");
coll.add(true);
System.out.println("coll:" + coll); // coll:[itheima, java, 666, true]
// public boolean contains(Object obj): // 判断当前集合中是否包含给定的对象。
System.out.println(coll.contains("itcast")); // false
System.out.println(coll.contains("it"));// true
// public int size(): // 返回集合中元素的个数。
System.out.println("集合中有:" + coll.size() + "个元素!");
// public Object[] toArray(): // 把集合中的元素,存储到数组中
Object[] obj = coll.toArray();
System.out.println(Arrays.toString(obj)); // [it, java, 666, true]
// public boolean isEmpty(): // 判断当前集合是否为空。
System.out.println(coll.isEmpty()); // false
// public boolean remove(E e): // 把给定的对象在当前集合中删除。
coll.remove(true);
System.out.println("coll:" + coll); // coll:[it, java, 666]
// public void clear(): // 清空集合中所有的元素。
coll.clear();
System.out.println("coll:" + coll); // coll:[]
System.out.println(coll.isEmpty()); // true
}
}
二、Iterator迭代器
2.1 Iterator接口
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.Iterator。
想要遍历Collection集合,那么就要获取该集合对应的迭代器来完成迭代操作,下面介绍一下获取迭代器的方法:
// Iterator接口里面的方法都需要使用对象来调用!所有的单列集合都有iterator()方法(就可以得到Iterator接口的实例对象)
public Iterator iterator(): // 获取集合对应的迭代器,用来遍历集合中的元素的
**迭代:**即Collection集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出来,一直把集合中的所有元素全部取出。
Iterator接口的常用方法
public boolean hasNext(): // 如果仍有元素可以迭代,则返回 true。
public E next(): // 返回迭代的下一个元素。
代码演示:
/*
迭代器Iterator: 获取集合中所有的数据
在获取数据之前,先判断集合里面是否有数据,有就取出来,然后继续判断继续取,一直到判断集合里面没有数据的时候就不取了。
使用迭代器:
1. 通过集合对象获得迭代器对象
2. 调用迭代器的hasNext()方法判断集合中是否有数据
3. 只要有数据,那么就调用next()方法获取集合中的数据!
*/
public class Test01 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 判断
while(iterator.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
}
}
}
注意1:在进行集合元素获取时,如果集合中已经没有元素了,还继续使用迭代器的next方法,将会抛出java.util.NoSuchElementException没有集合元素异常
public class Test01 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 判断
while(coll.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
}
System.out.println(iterator.next()); // 迭代器中的元素数据已经取完了,还在获取,就会出现这个没有此元素异常!
}
}
注意2:在进行集合元素获取时,如果添加或移除集合中的元素 , 将无法继续迭代 , 将会抛出ConcurrentModificationException并发修改异常.
public class Test02 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// 通过集合对象获得迭代器对象
Iterator iterator = coll.iterator(); // 将集合的数据封装到迭代器中去了
// 此时集合和迭代器是2个内容(里面的数据是一样的!)
while(iterator.hasNext()){
// 获取下一个数据
Object obj = iterator.next();
System.out.println("obj:"+ obj);
// 修改集合的数据(增删改)
coll.add("完犊子了。。。"); // ConcurrentModificationException:并发修改异常!
}
}
}
2.2 迭代器的实现原理
当遍历集合时,首先通过调用集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。在调用Iterator的next方法之前,迭代器的索引位于第一个元素之前,不指向任何元素,当第一次调用迭代器的next方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next方法时,迭代器的索引会指向第二个元素并将该元素返回,依此类推,直到hasNext方法返回false,表示到达了集合的末尾,终止对元素的遍历。
迭代器不允许在迭代数据的过程中对集合中的数据进行修改操作,但是之前的普通循环遍历是可以的!
下列说法错误的是:( B )
A: 迭代器是用来迭代集合容器的;
B: 一个迭代器可以对一个集合迭代多次;
C: 迭代器只能从头到尾迭代,不能倒着迭代;
D: 迭代器迭代集合时,不能使用迭代器之外的方法修改集合的长度
2.3 增强for
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删改操作。
* 普通for格式:
// 在遍历集合的过程中,可以对集合中的元素数据进行修改(增删改)操作!
for(int i=0;i<list.size();i++){
// 获得单个对象的数据
Object obj = list.get(i);
}
* 增强for格式:
// 在遍历集合的过程中,不能对集合中的元素数据进行修改(增删改)操作!【底层是迭代器】
for(数据类型 变量名称:集合对象或者数组){
}
* 注意:
① 增强for循环必须有被遍历的目标,目标只能是Collection或者是数组;
② 增强for(迭代器)仅仅作为遍历操作出现,不能对集合进行增删改元素操作,否则抛出ConcurrentModificationException并发修改异常
public class Test03 {
public static void main(String[] args) {
// 创建集合对象
Collection coll = new ArrayList(); // 多态
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// 普通遍历
/*
for (int i = 0; i < coll.size(); i++) {
// 需要调用子类独有的方法,向下转型
ArrayList list = (ArrayList) coll;
// 得到单个对象
Object o = list.get(i);
System.out.println("o:" + o);
list.remove(o); // 可以的!
}
*/
// 增强for循环 【快捷键:被遍历集合对象.iter+回车】
for (Object obj : coll) { // 【集合在创建的时候没有泛型类型,默认是Object类型,那么遍历集合coll得到的单个对象的数据类型就是Object】
System.out.println("obj:" + obj);
// 删除数据
// coll.remove(obj); // 并发修改异常ConcurrentModificationException
}
}
}
三、泛型
3.1 泛型概述
在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
/*
未使用泛型的缺点!
1. 需要单独使用存储数据自己的功能方法的时候,需要强转!
2. 没有使用泛型,意味着可以存放任意类型的数据,在强转的过程中,可能出现ClassCastException
*/
public class Test01 {
public static void main(String[] args) {
// 创建集合对象[没有指定集合里面存放元素数据的类型,那么就是Object类型的]
ArrayList coll = new ArrayList();
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// coll.add(true); // 没法转成字符串的!【希望一个集合只存放同一类型的数据】
// 遍历
for (int i = 0; i < coll.size(); i++) {
// 获取集合中单个元素数据(Object类型)
Object o = coll.get(i);
// 用不了字符串的方法(charAt),强转
String s = (String) o;
char c = s.charAt(1);
System.out.println(c);
}
}
}
泛型:可以在类或方法中预支地使用未知的类型。它是数据类型的一部分,我们将类名与泛型合并一起看做数据类型。
集合是一个类(是对象的数据类型),泛型指的是集合中元素数据的类型!
一般在创建对象时,将未知的类型确定具体的类型。当没有指定泛型时,默认类型为Object类型。
3.2 使用泛型的好处
① 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
② 避免了类型强转的麻烦。
/*
未使用泛型的缺点!
1. 需要单独使用存储数据自己的功能方法的时候,需要强转!
2. 没有使用泛型,意味着可以存放任意类型的数据,在强转的过程中,可能出现ClassCastException
泛型好处:
1. 使用泛型后,对集合中的元素数据进行了类型的限定,从而将运行时期的异常提前到编译阶段!
2. 不用强转!
*/
public class Test02 {
public static void main(String[] args) {
// 创建集合对象[指定集合里面存放元素数据的类型是String,向集合中只能添加String类型的数据]
ArrayList<String> coll = new ArrayList<>();
// 添加数据到集合中去
coll.add("it");
coll.add("itnb");
coll.add("javaee");
// 添加失败(将运行阶段的问题提升到编译阶段)
// coll.add(true);
// 遍历
for (int i = 0; i < coll.size(); i++) {
// 获取集合中单个元素数据(Object类型)
String s = coll.get(i);
System.out.println(s.charAt(1));
}
}
}
扩展:泛型仅仅只是针对编译阶段有效!在运行时期是会被擦除的(运行时期代码的类型是限制不了的)!
/*
此块代码不用大家掌握,能够理解:泛型仅仅只是针对编译阶段有效!在运行时期是会被擦除的(运行时期代码的类型是限制不了的)
*/
public class Test03 {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 定义集合对象
ArrayList<String> list = new ArrayList<>();
list.add("it");
list.add("itnb");
list.add("javaee");
//list.add(true); // 由于设定了泛型类型,在编译阶段会进行语法检测(添加的数据只要不是String类型的,就会报错!)
// 本质:集合是可以存放任意类型的数据的!【以下代码:在运行阶段执行了list的add方法将boolean类型的true值添加到list集合中去】
Class aClass = list.getClass();
Method add = aClass.getMethod("add", Object.class);
add.invoke(list,true); // 将boolean类型的true值添加到list集合中去
System.out.println("list:"+ list); // list:[itheima, itcast, javaee, true]
}
}
3.3 泛型的定义与使用
泛型可以灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
3.3.1 定义和使用含有泛型的类
* 定义格式:
修饰符 class 类名<代表泛型的变量> { }
例如:ArrayList的定义 ======>>>> public class ArrayList<E>{}
* 确定泛型类型:
在创建对象的时候确定泛型
// 定义含有泛型的类!
public class MyArrayList<A>{ // A是一个预支的类型,在创建对象的时候来进行确定,一旦确定,那么操作对象的时候,指定的类型就只能是创建对象时的类型
public static void main(String[] args){
// 在创建对象的时候确定泛型
MyArrayList<String> list1 = new MyArrayList<>(); // list1以后在编译阶段只能操作String类型的数据
// 在创建对象的时候确定泛型
MyArrayList<Boolean> list2 = new MyArrayList<>();// list2以后在编译阶段只能操作Boolean类型的数据
}
// 定义了很多其它功能的方法,比如向集合中添加数据的方法
}
// 泛型类的学习,可以参考ArrayList集合!!!
3.3.2 含有泛型的方法
* 定义格式:
修饰符 <代表泛型的变量类型> 返回值类型 方法名(参数){ }
* 确定泛型类型:
调用方法时,确定泛型的类型
/*
泛型方法:
修饰符 <代表泛型的变量类型> 返回值类型 方法名(参数){ }
泛型的类型什么时候确定:调用方法的时候!
*/
public class Test05 {
public static void main(String[] args) {
show1("xxx");
String xxx = show2("xxx");
Boolean flag = show2(true);
}
public static <VIP> void show1(VIP vip){
System.out.println("xxxx");
}
public static <VIP> VIP show2(VIP vip){
System.out.println("xxxx");
return vip;
}
}
3.3.3 含有泛型的接口
* 定义格式:
修饰符 interface 接口名<代表泛型的变量> { }
例如:public interface List<E>
* 确定泛型类型:
①定义类时确定泛型的类型
②始终不确定泛型的类型,直到创建对象时,确定泛型的类型
public interface MyCollection<A> { // public interface Collection<E>{}
public void add(A a);
}
public interface MyList<A> extends MyCollection<A> { // public interface List<E> extends Collection<E>{}
}
/*
* 定义格式:类似泛型类!
修饰符 interface 接口名<代表泛型的变量> { }
例如:public interface List<E>
* 确定泛型类型:
①定义类时确定泛型的类型
②始终不确定泛型的类型,直到创建对象时,确定泛型的类型
*/
public class MyArrayList<A> implements MyList<A> {
@Override
public void add(A a) {
// 。。。。。。
}
public static void main(String[] args) {
// 创建对象的时候确定泛型类型
MyArrayList<String> ml1 = new MyArrayList<>(); // 此时代表接口中泛型变量A的类型是String类型
MyArrayList<Integer> ml2 = new MyArrayList<>();// 此时代表接口中泛型变量A的类型是Integer类型
ml2.add(10);
ml1.add("xxx");
//ml1.add(10); // 编译报错!ml1里面的泛型类型为String类型
}
}
/*
确定泛型类型:
定义类时确定泛型的类型
*/
public class MyArray<E> implements MyList<Boolean> { // 虽然确定了接口的泛型类型,然后在定义类的时候也定义了泛型(变量)
@Override
public void add(Boolean flag) {
}
public static void main(String[] args) {
// 创建对象
MyArray<Integer> ml = new MyArray<>(); // 使用另外的一种确定泛型类型的方式(创建对象!) 【MyArray相当于是一个泛型类】
}
}
/*
确定泛型类型:
定义类时确定泛型的类型
*/
public class MyArray implements MyList<Boolean>{
public static void main(String[] args) {
// 创建对象
//MyArray<Integer> ml = new MyArray<>(); // 使用用另外的一种确定泛型类型的方式(创建对象!)
MyArray m2 = new MyArray();
m2.add(true); // 给定Boolean类型 【在MyList接口后面没有写泛型类型,那么调用这个方法可以给定任意类型!】
}
@Override
public void add(Boolean b) {
}
}
3.4 泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
public class Test06 {
public static void main(String[] args) {
// 创建2个集合对象
Collection<String> coll1 = new ArrayList<>();
Collection<Integer> coll2 = new ArrayList<>();
// 调用方法
add(coll1); // coll1和coll2是Colleciont接口的子类对象!
add(coll2); // 若add方法的Collection泛型类型定义为String的时候,此时coll2就添加不进去了!
//ArrayList<Object> list = new ArrayList<String>(); // 编译报错! 泛型类型不能使用继承!(必须要求2个泛型的类型一致!)
}
// 这个方法:后面调用的时候,传递进来的Colleciont子类对象的泛型类型是不确定的!也就是说明参数Colletion的泛型类型不能确定写死!
public static void add(Collection<?> coll){ // 定义方法给定父接口类型(多态)
// 此时只能接受数据,不能往该集合中存储数据
//coll.add("xxx"); // 编译报错!
}
}
泛型不存在继承关系(左右两侧得一致)
boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
public class Test07 {
public static void main(String[] args) {
Collection<String> coll1 = new ArrayList<>();
coll1.add("it");
coll1.add("itnb");
Collection<String> coll2 = new ArrayList<>();
coll2.add("itnb");
// boolean containsAll(Collection<?> c) 如果此集合包含指定集合中的所有元素,则返回 true 。
System.out.println(coll1.containsAll(coll2)); // true
Collection<Integer> coll3 = new ArrayList<>();
coll3.add(100);
coll3.add(99);
Collection<Integer> coll4 = new ArrayList<>();
coll4.add(25);
System.out.println(coll3.containsAll(coll4)); // false
}
}
3.5 受限泛型
之前设置泛型的时候,实际上是可以任意设置的,只要是类就可以设置。但是在JAVA的泛型中可以指定一个泛型的上限和下限。
?表示泛型通配符,如果要对?泛型通配符的取值范围进行限制,可以使用泛型限定
3.5.1 泛型的上限
* 格式:
类型名称 <? extends 类 > 对象名称
* 意义:
只能接收该类型及其子类
3.5.2 泛型的下限
* 格式:
类型名称 <? super 类 > 对象名称
* 意义:
只能接收该类型及其父类型
比如:现已知Object类,String 类,Number类,Integer类,其中Number是Integer的父类
public class Test08 {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement1(list1);
getElement1(list2); // 报错!
getElement1(list3);
getElement1(list4); // 报错!
getElement2(list1); // 报错!
getElement2(list2); // 报错!
getElement2(list3);
getElement2(list4);
}
// 当前这个方法内部集合的泛型类型只能是: Number类型和它的子类Integer类型
public static void getElement1(Collection<? extends Number> coll) {
}
// 当前这个方法内部集合的泛型类型只能是: Number类型和它的父类Object类型
public static void getElement2(Collection<? super Number> coll) {
}
}