1.1 集合概述
1.什么是集合:
- 一个用来存储多个元素的容器。
2.数组和集合的区别:
- 数组:长度固定,可以存储基本数据类型 ,也可以存储引用数据类型。
- 集合:长度可变,只能存储引用数据类型,如果存储基本数据类型则需要使用对应的包装类。
3.集合的分类:
- 单列集合(Collection):每次存储元素时只存储一个元素。
- 双列集合(Map):每次存储元素时要 存储两个元素。
1.2 Collection集合体系
- Collection:父接口 所有单列集合的父类
- List:子接口
- ArrayList:实现类
- LinkedList:实现类
- Vector:实现类
- Set:子接口
- HashSet:实现类
- LinkedHashSet:实现类
- 其中,橙色框里写的都是接口类型,而蓝色框里填写的都是具体的实现类。
- 集合本身是一个工具,它存放在Java.util包中。在Collection接口定义着单列集合框架中最共性的内容。
1.3 Collection常用方法
- public boolean add(E e):添加元素,添加成功返回true,否则false。
- public void clear():将集合元素清空。
- boolean remove(E e)
- 删除指定元素,删除成功返回true,否则false。
- 如果有多个相同的元素,默认只会删除第一个。
- boolean contains(E e):判断集合是否包含指定元素,包含返回true,否则false。
- public boolean isEmpty():判断集合是否为空(元素个数是否为0),是返回true,否则false。
- public int size():获得元素的个数。
- public Object[] toArray()
- 将集合中的元素添加到数组返回
- 集合转数组
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
//创建集合对象
Collection<String> coll=new ArrayList<String>();
//添加元素
coll.add("aaa");
coll.add("bbb");
coll.add("ccc");
coll.add("ddd");
System.out.println(coll);
//删除元素
System.out.println(coll.remove("aaa"));// true
System.out.println(coll);
//判断集合中是否包含元素
System.out.println(coll.contains("eeee"));//false
//集合转数组
Object [] objs=coll.toArray();
//输出数组中的元素
System.out.println(Arrays.toString(objs));//[bbb, ccc, ddd]
//清空集合元素
coll.clear();
System.out.println(coll);//[]
//判断集合是否为空
System.out.println(coll.isEmpty());//true
}
}
2.1 迭代器概述
1.什么是迭代器?
- 一个用来遍历集合的对象,该对象实现了Iterator接口
- 对Collection进行遍历的迭代器
2.如何获得迭代器对象?
- 通过集合对象调用该方法:Iterator<E> iterator()获得迭代器对象。
3. Iterator接口常用方法?
- boolean hasNext() 判断是否有下一个元素,有返回true,否则false
- E next() 先将指针下移指向下一个元素,并将当前指针指向位置的元素作为返回值返回。
2.2 Iterator示例代码
import java.util.ArrayList;
import java.util.Iterator;
public class IteratorDemo01 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list=new ArrayList<>();
//添加元素
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
//使用普通for循环遍历
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("--------------------");
//获得迭代器对象
Iterator<String> it=list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
2.3 迭代器的好处
- 屏蔽了众多集合的内部实现,对外提供统一的遍历方式。
- 所有的单列集合都可以使用迭代器进行遍历。
2.4 迭代器的注意事项
- hasNext方法仅仅是判断是否有下一个元素,不会移动指针位置。
- hasNext方法和next方法必须成对出现,调用一次hasNext就对应一次next方法。
- 使用迭代器遍历集合时不能对集合进行增删操作,否则会抛出异常。
- java.util.ConcurrentModificationException: 并发修改异常
- 在使用迭代器遍历集合的过程中对集合元素进行了增删操作,则会抛出该异常。
3.1 增强for概述
1.增强for概述
- JDK1.5新特性
- 专门用来遍历集合和数组
- 本质:迭代器
2.增强for格式
- for(数据类型 变量名:数组名或集合名){ //循环体}
3.2 增强for遍历数组
public class ForEachDemo01 {
public static void main(String[] args) {
//定义数组
int [] arr={1,2,3,4,5,6};
//使用增强for遍历数组
for(int num: arr){
System.out.println(num);
}
}
}
3.3 增强for遍历集合
import java.util.ArrayList;
/*
增强for遍历集合:存储自定义对象
*/
public class ForEachDemo03 {
public static void main(String[] args) {
// 创建集合对象
ArrayList<Student> list = new ArrayList<>();
// 添加学生对象
list.add(new Student("小泽", 20));
list.add(new Student("小苍", 24));
list.add(new Student("小波", 26));
list.add(new Student("小武", 28));
// 使用增强for
for (Student stu : list) {
System.out.println(stu);
// 修改姓名
stu.setName("lily");
}
System.out.println("----------");
// 使用增强for
for (Student stu : list) {
System.out.println(stu);
}
}
}
4.1 泛型在集合中的使用
- 创建集合同时指定集合存储的数据类型
- 指定数据类型时,要么指定左边,要么两边都执行相同的数据类型
- 在JDK1.7之前,必须两边都要指定并且要相同的数据类型
- 在JDK1.7之后,指定左边即可
- 在泛型中没有多态的概念
4.2 泛型在集合中使用的好处
- 将运行时错误转换为编译期错误,增强了集合的安全性。
- 省去了数据类型强制转换的麻烦。
- 示例代码
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo04 {
public static void main(String[] args) {
//创建集合对象
ArrayList<String> list=new ArrayList<String>();
list.add("aa");
list.add("bb");
list.add("cc");
for(String str:list){
System.out.println(str.length());
}
}
}
4.3 泛型的定义与使用
1.泛型的概述
- JDK1.5新特性。
- 泛型可以使用在方法上,类上,接口上。
- 泛型变量可以理解为是某种数据类型的占位符。
- 泛型变量还可以理解为是某种数据类型的变量。
- 泛型变量的命名规则:只要是合法的标识符就可以,一般使用一个大写字母表示
a.常用的泛型变量名有:T type E element K key V value
2.泛型方法的概念
- 在定义方法时定义了泛型变量的方法就是泛型方法 。
3.泛型方法的定义格式
- 修饰符 <T> 返回值类型 方法名(参数列表){}
4.泛型方法的注意事项
- 泛型变量的具体数据类型是由调用者调用方法时传参决定。
- 泛型变量的具体数据类型不能是基本数据类型,如果要使用基本数据类型则需要使用对应的包装类类型。
- 泛型方法示例代码1
public class Demo01 {
public static void main(String[] args) {
Integer in=test(456);
String str=test("bcd");
Student stu=test(new Student());
Double d=test(0.5);
int age=10;
System.out.println(age);
}
//泛型方法
public static <T> T test(T param){
T a=null;
return a;
}
}
- 泛型方法示例代码2
/*
定义一个数组工具类MyArrays ,提供两个成员方法,方法参数接收一个任意类型的数组
一个方法的功能是将数组的元素反转.
一个方法的功能是将数组的元素拼接成一个字符串返回。
字符串格式:"[1,2,3,4,5]"
*/
public class MyArrays{
/*
使用泛型方法实现:方法参数接收一个任意类型的数组
一个方法的功能是将数组的元素反转.
*/
public static <T> void reverse(T[] arr){
for(int i=0,j=arr.length-1;i<j;i++,j--){
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
/*
使用泛型方法:定义一个方法的功能:将数组的元素拼接成一个字符串返回。
字符串格式:"[1,2,3,4,5]"
*/
public static <T> String toString(T [] arr){
//创建可变字符串
StringBuilder sb=new StringBuilder("[");
//遍历数组
for(T t:arr){
sb.append(t).append(",");
}
//删除最后一个元素
sb.deleteCharAt(sb.length()-1);
sb.append("]");
return sb.toString();
}
}
4.4 泛型类
1. 泛型类概述
- 在定义类的同时定义了泛型变量的类。
2.泛型类的定义格式
- class 类名<T>{ // 在该类中可以将泛型变量T当成一种数据类型使用。 }
3.泛型类的注意事项
- 泛型类泛型变量的具体数据类型是在创建该类对象时由创建者指定。
- 如果创建泛型类对象时没有指定泛型变量的具体数据类型,则默认是Object
- 静态方法不能使用类上定义的泛型变量,如果该方法中要使用泛型变量,则需要将该定义为泛型方法。
- 示例代码
public class MyArrays<T>{
/*
方法参数接收一个任意类型的数组
一个方法的功能是将数组的元素反转.
*/
public void reverse(T[] arr){
for(int i=0,j=arr.length-1;i<j;i++,j--){
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
/*
将数组的元素拼接成一个字符串返回。
字符串格式:"[1,2,3,4,5]"
*/
public String toString(T [] arr){
//创建可变字符串
StringBuilder sb=new StringBuilder("[");
//遍历数组
for(T t:arr){
sb.append(t).append(",");
}
//删除最后一个元素
sb.deleteCharAt(sb.length()-1);
sb.append("]");
return sb.toString();
}
}
//泛型类基本使用
public class Demo {
public static void main(String[] args) {
//定义字符串数组
String[] strs = {"a", "b", "c"};
//创建MyArrays对象
MyArrays<String> myArrays01=new MyArrays<>();
myArrays01.reverse(strs);
System.out.println(myArrays01.toString(strs));//[c,b,a]
//创建MyArrays对象
MyArrays<Integer> myArrays02=new MyArrays<>();
//创建整型数组
Integer[] arr={3,4,5,6,7,7};
myArrays02.reverse(arr);
System.out.println(myArrays02.toString(arr));//[7,7,6,5,4,3]
//创建MyArrays对象:不指定泛型变量的具体数据类型
MyArrays myArrays03=new MyArrays();
}
}
5.1 泛型接口
1. 泛型接口的概念
- 在定义接口的同时定义了泛型变量的接口
2.泛型接口的定义格式
- interface 接口名<T>{ // 可以将泛型变量T当成一种数据类型使用 }
3.泛型接口的实现方式
- 方式1:实现接口的同时指定泛型变量的具体数据类型。(不够灵活)
- 方式2:实现接口的时不指定泛型变量的具体数据类型,那么此时需要将该实现类定义为泛型类 ,由使用者创建实现类对象时指定泛型变量的数据类型。(推荐使用)
- 泛型接口示例代码
/*
分层开发:
1.表现层
2.业务层
3.数据访问层:直接和数据库打交道,对数据库进行增删改查操作。
*/
public interface Dao<T> {
//增
public void save(T t);
//删
public void delete(int id);
//改
public void update(T t);
//查
public T find(int id);
}
/*
方式1:实现接口的同时指定泛型变量的具体数据类型。
*/
public class ProductDao implements Dao<Product> {
@Override
public void save(Product product) {
System.out.println("增加产品");
}
@Override
public void delete(int id) {
}
@Override
public void update(Product product) {
}
@Override
public Product find(int id) {
return null;
}
}
/*
方式1:实现接口的同时指定泛型变量的具体数据类型。
*/
public class StudentDao implements Dao<Student> {
@Override
public void save(Student student) {
System.out.println("保存学生信息");
}
@Override
public void delete(int id) {
System.out.println("删除学生信息");
}
@Override
public void update(Student student) {
System.out.println("更新学生信息");
}
@Override
public Student find(int id) {
System.out.println("查找学生信息");
return null;
}
}
/*
实现接口的时不指定泛型变量的具体数据类型,那么此时需要将该实现类定义为泛型类,
由使用者创建实现类对象时指定泛型变量的数据类型。
*/
public class BaseDao<T> implements Dao<T> {
@Override
public void save(T t) {
}
@Override
public void delete(int id) {
}
@Override
public void update(T t) {
}
@Override
public T find(int id) {
return null;
}
}
public class Demo {
public static void main(String[] args) {
//创建学生对象
Student stu=new Student();
//创建StudentDao对象
StudentDao stuDao=new StudentDao();
stuDao.save(stu);
stuDao.update(stu);
stuDao.delete(1);
stuDao.find(2);
//创建产品对象
Product p=new Product();
//创建ProduceDao对象
ProductDao pd=new ProductDao();
pd.save(p);
// 创建BaseDao对象指定泛型变量是Student
BaseDao<Student> baseDao=new BaseDao<>();
baseDao.save(stu);
// 创建BaseDao对象指定泛型变量是Product
BaseDao<Product> baseDao2=new BaseDao<>();
baseDao2.save(p);
}
}
6.1 泛型上下限
6.1.1 泛型上下限的引入
需求1:
- 定义一个方法可以接收任意类型的集合对象
- 集合对象只能存储Integer或者是Integer的父类数据
需求2:
- 定义一个方法可以接收任意类型的集合对象
- 集合对象只能存储Number或者是Number的子类数据
6.1.2 泛型上下限的格式
1.泛型的上限
- 格式:<? extends 类>
- 意义:只能接收该类型及其子类
2.泛型的下限
- 格式:<? super类>
- 意义:只能接收该类型及其父类型
- 注意事项
- ? 代表泛型通配符,可以匹配任意的类型。
- 通配符?不能用在泛型方法、泛型类、泛型接口定义上。
- 通配符?一般不会单独使用,一般都会配合泛型上下限使用。
- ? 不能在方法体中当成一种数据类型使用
a.泛型上限
? extends Number:可以接收Number或Number子类类型的数据
b.泛型下限
? super Integer : 可以接收Integer或Integer父类类型的数据
import java.util.ArrayList;
public class Demo05 {
public static void main(String[] args) {
ArrayList<Number> list01=new ArrayList<>();
ArrayList<Integer> list02=new ArrayList<>();
ArrayList<Object> list03=new ArrayList<>();
print01(list01);
print01(list02);
print01(list03);
print02(list01);
print02(list02);
//print02(list03);
}
/*
需求2:定义一个方法可以接收任意类型的集合对象
集合对象只能存储Number或者是Number的子类数据
*/
public static void print02(ArrayList<? extends Number> list){
}
/*
需求1:定义一个方法可以接收任意类型的集合对象
集合对象只能存储Integer或者是Integer的父类数据
*/
public static void print01(ArrayList<? super Integer> list){
}
}