目录
集合框架
1.1 概述
1.2 集合和数组的区别
1.3 Collection集合
1.3.1 概述
1.3.2 常用方法
1.4 迭代器
1.4.1 概述
1.4.2 常用方法
1.4.3 使用步骤
1.5 增强for循环
1.5.1 概述
1.5.2 使用
1.6 泛型
1.6.1 概述
1.6.2 使用泛型的利弊
1.6.2.1 好处
1.6.2.2 弊端
1.6.3 定义和使用
1.6.3.1 泛型使用到类上
1.6.3.2 泛型使用到方法上
1.6.3.3 泛型使用到接口上
1.6.4 泛型的通配符
1.6.5 通配符的高级使用——受限泛型
集合框架
1.1 概述
在Java中,集合是一种用来存储和操作一组对象的容器。它提供了各种实现类和接口,可以根据需求选择合适的集合类型。常见的集合类型有List、Set和Map。List是一个有序的集合,可以包含重复的元素;Set是一个不允许重复元素的集合;Map是一种键值对的集合,每个键值对都是一个Entry对象,键是唯一的。集合框架提供了丰富的方法和功能,可以方便地对集合进行增删改查等操作。
1.2 集合和数组的区别
集合和数组是Java中两种不同的数据结构,它们的差异主要体现在以下几个方面:
-
数据类型:数组是一种固定长度的数据结构,它可以包含相同类型的元素。集合是一种可变长度的数据结构,可以包含不同类型的元素。
-
大小调整:数组的长度在创建时就被确定,无法动态改变。如果需要增加或删除元素,需要创建一个新的数组。集合的大小是可以动态调整的,可以根据需要添加或删除元素。
-
类型安全:数组可以包含任何类型的元素,包括基本数据类型和自定义类型。集合在泛型的支持下,可以确保只添加特定类型的元素,提供了类型安全的机制。
-
迭代和搜索:对于数组,可以通过下标来访问和修改元素,也可以使用循环遍历数组中的元素。集合提供了更多的迭代和搜索方法,例如迭代器和foreach循环。
-
功能:集合提供了很多便捷的方法和功能,例如排序、查找、去重等。数组相对较为简单,只提供了基本的访问和修改功能。
总的来说,数组在空间上更加高效,因为它们不需要额外的对象来维护元素的添加和删除。但集合在使用上更加灵活,提供了更多的功能和方便的操作方法。根据实际需求,选择使用数组还是集合是根据具体情况来决定的。
1.3 Collection集合
1.3.1 概述
java.util.Collection:单列集合的根接口,用于存储一系列符合某种规则的元素,里边定义了所有单列集合共性的方法,任意单列集合都可以使用其中的方法,且继承自java.util包中的Iterable接口,因此它的实现类可以通过迭代器进行遍历。它有List和Set两个重要的子接口。
1.3.2 常用方法
Collection的常用方法,它的所有实现类都可以使用。
修改集合:
boolean add(E e):向集合中添加元素,一般返回true。
boolean remove(E e):删除集合中的某个元素,存在元素(就删除元素,返回true),不存在元素(删除失败,返回false)。
void clear():清空集合中所有的元素,但是不删除集合,集合还在。
获取集合:
int size():获取集合的长度,返回集合中元素的个数。
Object[] toArray():将集合转成一个数组,可以通过索引获取数组元素。
判断集合:
boolean contains(E e):判断集合中是否包含某个元素,包含返回true,不包含返回false。
boolean isEmpty():判断集合是否为空,集合为空返回true,集合不为空返回false。
package com.zhy.coll;
import java.util.ArrayList;
import java.util.Collection;
public class CollectTest {
public static void main(String[] args) {
//Collection是一个接口,使用多态创建具体的实现类对象,
//Collection<String>是采用泛型写法,表示该集合只能存储String类型的元素,如果省略,表示可以存储Object类型的元素
Collection coll = new ArrayList();
//1.add(E e):向集合中添加元素
coll.add("张三");
coll.add(123);
coll.add(true);
System.out.println("初始化集合:" + coll);
//2.remove(E e):删除集合中的某个元素
coll.remove(true);
System.out.println("移除值为true的元素:" + coll);
//3.size():获取集合的长度
int length = coll.size();
System.out.println("集合的长度:" + length);
//4.toArray():将集合转成一个数组,遍历数组,可以通过索引获取数组元素
Object[] obj = coll.toArray();
System.out.print("遍历数组元素:");
for(int i = 0; i < obj.length; i++) {
System.out.print(obj[i] + " ");
}
//5.contains(E e):判断集合中是否包含某个元素
boolean flag = coll.contains(123);
System.out.println("\n集合中是否包含元素123:" + flag);
//6.isEmpty():判断集合是否为空
boolean isNull = coll.isEmpty();
System.out.println("清空集合前,集合是否为空:" + isNull);
//7.clear():清空集合中所有的元素
coll.clear();
System.out.println("清空集合中所有的元素:" + coll);
System.out.println("清空集合后,集合是否为空:" + coll.isEmpty());
}
}
1.4 迭代器
1.4.1 概述
java.util.Iterator:即Collection集合元素的通用遍历方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来;继续再判断,如果还有就再取出来,一直把集合中的所有元素取出,这种取出方式专业术语称为迭代。
1.4.2 常用方法
boolean hasNext():判断集合中还有没有下一个元素,有就返回true,没有就返回false。
E next():返回迭代器的下一个元素,取出集合中的下一个元素。
1.4.3 使用步骤
Iterator迭代器是一个接口,我们无法直接使用,需要使用Iterator接口的实现类对象,获取实现类的方式比较特殊,Collection接口中有一个方法,叫iterator(),这个方法返回的就是迭代器的实现类对象Iterator<E> iterator():返回在此collection的元素上进行迭代的迭代器。步骤如下:
- 使用集合中的方法iterator()获取迭代器的实现类对象,使用Iterator接口接收(多态)。
- 使用Iterator接口中的方法hasNext判断还有没有下一个元素。
- 使用Iterator接口中的方法next取出集合中的下一个元素。
package com.zhy.coll;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectTest {
public static void main(String[] args) {
//1.创建集合对象
Collection coll = new ArrayList();
//2.将集合中添加元素
coll.add("张三");
coll.add(123);
coll.add(true);
System.out.println("初始化集合:" + coll);
//3.获取迭代器实现类,多态写法
Iterator iterator = coll.iterator();
//4.使用迭代器遍历集合元素
System.out.print("使用迭代器遍历集合:");
while (iterator.hasNext()) {
//取出集合的下一个元素,如果集合为空,会抛出异常NoSuchElementException(没有元素异常)
//所以要和iterator.hasNext()组合使用,先判断,在取值。
Object obj = iterator.next();
System.out.print(obj + " ");
}
}
}
1.5 增强for循环
1.5.1 概述
底层使用的也是迭代器,使用for循环的格式,简化了迭代器的书写,是JDK1.5之后出现的新特性,用来遍历集合和数组,因为没有索引,所以不能对集合中的元素进行增删操作。
1.5.2 使用
格式:
for(集合/数组中元素的数据类型 变量名:集合名/数组名){
System.out.println("变量名");
}注:增强for循环必须有被遍历的目标,目标只能是集合或者数组。
package com.zhy.coll;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectTest {
public static void main(String[] args) {
//1.创建集合对象
Collection coll = new ArrayList();
//2.将集合中添加元素
coll.add("张三");
coll.add(123);
coll.add(true);
System.out.println("初始化集合:" + coll);
//3.使用增强for遍历集合
System.out.print("使用增强for遍历集合:");
for(Object obj : coll) {
System.out.print(obj + " ");
}
}
}
1.6 泛型
1.6.1 概述
是一种未知的数据类型,当我们不知道使用什么数据类型的时候,可以使用泛型,也可以看做是一个变量,用来接收数据类型。
用法: 是在容器后面添加<Type>,Type可以是类,抽象类,接口,表示只能存放这种类型或者其子类。
E e:Element 元素
T t:Type 类型
例如:ArrayList集合在定义的时候,不知道集合中都会存储什么类型的数据,所以类型定义为泛型,创建集合对象的时候,就会确定泛型的数据类型。
1.6.2 使用泛型的利弊
1.6.2.1 好处
- 避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型。
- 把运行期异常(代码运行之后会抛出的异常),提升到了编译期(写代码的时候就会报错)。
1.6.2.2 弊端
- 泛型是什么类型,就只能存储什么类型的数据;如果不使用泛型的话,默认类型就是Object,可以存储任意类型的数据;但是不安全,容易引发异常。
综合考虑,使用泛型的好处大于弊端,推荐使用泛型。
package com.zhy.coll;
import java.util.ArrayList;
import java.util.List;
public class CollectTest {
public static void main(String[] args) {
//创建不带泛型的集合,那可以存储任意类型的数据
List list = new ArrayList();
list.add(123);
list.add("张三");
//创建带<String>泛型的集合,只能存储字符串类型的数据
//如果存储非String类型的数据,会编译报错
List<String> list2 = new ArrayList<String>();
list2.add("Annie");
list2.add("Bob");
}
}
1.6.3 定义和使用
1.6.3.1 泛型使用到类上
定义一个含有含有泛型的类,模拟ArrayList集合,泛型是一个未知的数据类型,当我们不确定需要什么数据类型的时候,可以使用泛型,泛型可以接收任意类型的数据,可以是Integer、String...,创建对象的时候确定泛型的数据类型。
格式:
修饰符 class 类名<代表泛型的变量>
注意:
需要在所有使用数据类型的地方都改成泛型,包括类、属性、方法、局部变量的数据类型。
package com.zhy.coll;
public class GenericClass<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
package com.zhy.coll;
public class CollectTest {
public static void main(String[] args) {
//创建GenericClass对象,泛型指定为String类型,就是将定义类中的所有E都换成String
GenericClass<String> geClass = new GenericClass<String>();
geClass.setName("张三");
System.out.println(geClass.getName());
//创建GenericClass对象,泛型指定为Integer类型,就是将定义类中的所有E都换成Integer
GenericClass<Integer> geClass2 = new GenericClass<Integer>();
geClass2.setName(123);
System.out.println(geClass2.getName());
}
}
1.6.3.2 泛型使用到方法上
定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间。
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表 使用泛型){
方法体;
}
注意:
含有泛型的方法,在调用方法的时候确定泛型的数据类型,传递什么类型的参数,泛型就是什么类型。
1.6.3.3 泛型使用到接口上
泛型应用到接口上,有两种形式。一是接口定义为泛型,接口的实现类,指定接口的泛型。二是接口使用什么类型,实现类就使用什么类型,创建对象时确定。
格式:
修饰符 interface 接口名<泛型>{}
使用:
方式一:Scanner类实现了Iterator接口,并指定接口的泛型为String,所有重写了next方法泛型默认就是Stringpublic interface Iterator<E> { E next(); }
public final class Scanner implements Iterator<String>, Closeable { public String next() {//……} //…… }
方式二:类跟着接口走,就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型。
public interface List<E> extends Collection<E> { void add(int index, E element); E get(int index); //…… }
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean add(E e) {//……} public E get(int index) {//……} //…… }
1.6.4 泛型的通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以使用通配符<?>表示,但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
?:代表任意的数据类型,不能创建对象使用,只能作为方法的参数使用。
1.6.5 通配符的高级使用——受限泛型
之前指定泛型的时候,实际上是可以任意设置的,只要是类就可以设置,但是在JAVA的泛型中可以指定一个泛型的上限和下限。
泛型的上限:
格式:类型名称 <? extends T> 对象名称
含义:表示只能接收T类型及其子类。所以往集合里面取出来的元素一定可以转型为T类型,是安全的。但是往集合放元素,可能放的子类A的数据,但实际是子类B的泛型,无法确定类型,所以会编译报错。
泛型的下限:
格式:类型名称 <? super T> 对象名称
含义:表示只能接收T类型及其父类。所有对象都继承Object对象,所以不管往集合里面放入什么类型的数据,Object都可以接收,是安全的。但是如果从集合里面取元素,无法确定类型,用实际类型接收不可取。总结:
1.如果希望只取出,不插入,就使用 "? extends T"。
2.如果希望只插入,不取出,就使用 "? super T"。
3.如果希望,又能插入,又能取出,就不要用通配符 "?"。
4.子类泛型和父类泛型 不可以像对象那样进行子父类之间的转换。
package com.zhy.coll;
import java.util.ArrayList;
public class TestGeneric {
/**
* 遍历Animal及其子类的集合
* @param list
*/
public static void iterate(ArrayList<? extends Animal> list) {
for (Animal animal : list) {
System.out.println(animal.name);
}
}
public static void main(String[] args) {
//传递Animal对象集合
ArrayList<Animal> animals = new ArrayList<>();
iterate(animals);
//传递Animal的子类对象Dog集合
ArrayList<Dog> dogs = new ArrayList<>();
iterate(dogs);
}
}