【Collection、泛型】
一、集合:
其实就是一个容器。在Java中集合的代表是Collection.
因为开发中存在很多的数据需要存储和处理,所以需要用到容器(集合)。
1.数组:
是可以存放“特定类型”的元素,数组的一旦定义出来长度就固定了。
2.集合:
默认是可以存放任意类型的元素,集合的大小是不固定的,添加的元素
多集合的大小就大。去掉了一个元素,集合的大小就变小。
二、Collection集合的体系结构:
什么是体系结构
集合的根类是Collection.
今天学习的所有的集合都是继承自Collection类的。
就像地图一样,会分为(平面地图,3D地图,全景地图,导航地图),每一种地图都有自己的使用场景和特有功能。
集合也是存在很多种的,每种集合有不同的功能和特点差异。
集合的体系:
集合的特点:
-
- Set系列集合特点:添加进去的元素是无序,不重复的,无索引的。
- – HashSet(实现类): 添加进去的元素是无序,不重复的。
- – LinkedHashSet(实现类) : 添加进去的元素是有序,不重复的。
-
- List系列集合特点:添加进去的元素是有序,有索引,可重复的。
- – ArrayList(实现类) :添加进去的元素是有序,有索引,可重复的。
- – LinkedList(实现类) :添加进去的元素是有序,有索引,可重复的。
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
public class CollectionDemo01 {
public static void main(String[] args) {
// 1.创建一个Set集合对象。
// 特点:添加进去的元素是无序,不重复的。
Collection sets = new HashSet();
sets.add("Java");
sets.add("Java");
sets.add("Java EE");
sets.add("Mysql");
sets.add("MyBatis");
sets.add(12);
sets.add(false);
System.out.println(sets);
// 添加进去的元素是有序,有索引,可重复的。
Collection lists = new ArrayList();
lists.add("Java");
lists.add("Java");
lists.add("Java EE");
lists.add("Mysql");
lists.add("MyBatis");
lists.add(12);
lists.add(false);
System.out.println(lists);
}
}
输出结果:
[Java EE, Java, false, Mysql, MyBatis, 12]
[Java, Java, Java EE, Mysql, MyBatis, 12, false]
三、Collection的常用功能:
1.Collection的常用功能:
集合是存在很多的操作功能的。开发中集合中的数据经常需要被添加,删除,判断是否包含,判断个数,清空集合等…
public boolean add(E e)`: 把给定的对象添加到当前集合中 。
public void clear()` :清空集合中所有的元素。
public boolean remove(E e)`: 把给定的对象在当前集合中删除。
public boolean contains(Object obj)`: 判断当前集合中是否包含给定的对象。
public boolean isEmpty()`: 判断当前集合是否为空。
public int size()`: 返回集合中元素的个数。
public Object[] toArray()`: 把集合中的元素,存储到数组中
总结:
1.集合的大小是使用size()方法,数组的长度是使用length属性获取.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
// 1.创建一个集合对象啊!!
Collection<String> cls = new ArrayList<>();
// 把给定的对象元素添加到当前集合中去 。
// 成功把元素添加进入了集合会返回true,反之!
cls.add("Java");
cls.add("徐干");
cls.add("徐冰");
//System.out.println(cls.add("Java EE"));
System.out.println(cls);
// 2.clear():清空集合中所有的元素。
//cls.clear();
//System.out.println(cls);
// 3.remove(E e):在集合中去删除指定的某个元素。
// 删除成功返回true,没有删除元素返回false
System.out.println(cls.remove("Java")); // true
System.out.println(cls.remove("Java1")); // false
System.out.println(cls);
// 4. contains(Object obj):判读集合中是否包含了某个元素,包含会返回true,
// 不包含返回false.
System.out.println(cls.contains("徐干")); // true
System.out.println(cls.contains("徐干1")); // false
// 5.isEmpty(): 判断当前集合是否为空 ,为空返回true,不为空返回false
/*System.out.println(cls.isEmpty()); //false
cls.clear(); // 清空集合
System.out.println(cls.isEmpty()); //true*/
// 6.size();集合的大小。
// 集合是一个容器大小的概念:size()是一个方法。
// 数组是连续的区域,元素大小是固定的,数组的长度是固定:数组的长度是length属性。
System.out.println(cls.size());
// 7.public Object[] toArray()`: 把集合中的元素,存储到数组中
// 集合一旦数据都添加完成了,其实集合的大小也确定了。
// 在开发的时候,有时候别人可能需要你传入一个数组。
// cls == [徐干, 徐冰]
Object[] objs = cls.toArray();
System.out.println("数组内容:"+ Arrays.toString(objs));
}
}
四、集合的遍历方式一迭代器方式
Collection集合的遍历方式有几种呢?
有三种:
- 1.集合的迭代器遍历集合。
- 2.增强for循环(foreach遍历)
- 3.JDK 1.8 之后的新技术。
1.迭代器遍历集合:
-
步骤:
- (1)创建一个集合对象,然后添加一些元素。
- (2)得到集合的迭代器。调用Iterator iterator()方法得到当前集合的迭代器。
- (3)使用迭代器遍历集合。next()方法去获取下一个元素。
-
总结迭代器的使用流程:
-
(1)得到当前集合的迭代器,通过集合调用集合的iterator()得到一个迭代器it.Iterator it = lists.iterator();
-
(2)定义一个while循环,每次通过it.hashNext()询问是否有下一个元素,有返回true,进入循环体中执行it.next()取出下一个元素,以此类推…如果最终it.hasNext()询问是否有下一个元素返回的是false,循环遍历结束!
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo01 {
public static void main(String[] args) {
// (1)创建一个集合对象,然后添加一些元素。
Collection<String> lists = new ArrayList<>();
lists.add("Java");
lists.add("Java EE");
lists.add("Mysql");
lists.add("Servlet");
System.out.println(lists);
// (2)得到集合的迭代器。
// 调用Iterator<E> iterator()方法得到当前集合的迭代器。
// 集合自己提供了iterator()方法去得到一个所谓的迭代器:Iterator
Iterator<String> it = lists.iterator();
// (3)使用迭代器遍历集合。
// lists = [Java, Java EE, Mysql, Servlet]
// it
/**
// 调用 next()方法去获取下一个元素
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
// NoSuchElementException :没有这个元素异常!
//System.out.println(it.next());
*/
// 使用循环改进
// it.hasNext() 询问是否有"下一个"元素,有则返回true,没有返回false
while(it.hasNext()){
String ele = it.next();
System.out.println(ele);
}
}
}
2.增强for循环
增强for循环遍历集合或者数组:
增强for循环有时候也称为foreach遍历。
增强for循环遍历的关键是要清楚增强for循环的写法格式:
for(被遍历集合或者数组中元素的类型 变量 : 被遍历集合或者数组 ){
......
}
注意:
1.增强for循环可以用于遍历集合或者数组。增强for的关键是要记住他的遍历格式!
2.迭代器是属于集合的,迭代器只能遍历当前集合。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
// 1.创建一个数组
String[] names = new String[]{"徐冰","徐干","徐霞客","詹天佑"};
// name
for(String name : names){
System.out.println(name);
}
System.out.println("---------------------------------------");
int[] arrs = {10,20,30,40};
for(int num : arrs){
System.out.println(num);
}
System.out.println("---------------------------------------");
Collection<String> nums = new ArrayList<>();
nums.add("Java");
nums.add("Spring");
nums.add("Spring Boot");
nums.add("Redis");
for(String ele : nums){
System.out.println(ele);
}
System.out.println("---------------------------------------");
Collection<Integer> scores = new ArrayList<>();
scores.add(20);
scores.add(30);
scores.add(60);
scores.add(90);
for(int s : scores){
System.out.println(s);
}
}
}
3.JDK8之后的新技术
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
Collection<String> nums = new ArrayList<>();
nums.add("Java");
nums.add("Spring");
nums.add("Spring Boot");
nums.add("Redis");
// 现阶段了解即可,为了笔记的完整性在这里介绍一下。
nums.forEach(s -> {
System.out.println(s);
});
/* nums.forEach(s -> System.out.println(s));
nums.forEach(System.out::println);*/
}
}
五、泛型
泛型:
- 泛型实际上是用来约束一个类可以操作的具体数据类型。
- 例如:集合如果不加泛型,它可以存放任意类型的元素。
- 引入泛型以后,实际上可以理解为泛型就是一个标签,它对当前类可以操作的具体数据类型就产生了编译时约束。
- 格式: 类<泛型>
- 注意:泛型是不支持基本数据类型的。泛型只支持引用数据类型(如果要用基本数据类型,可以选择包装类代替)从JDK 1.7之后泛型后面的类型申明可以省略。
import java.util.ArrayList;
import java.util.Collection;
public class FanXingDemo01 {
public static void main(String[] args) {
Collection cls = new ArrayList();
cls.add(12);
cls.add(99.99);
cls.add(false);
cls.add('a');
cls.add("Java EE");
Collection<String> cls1 = new ArrayList<String>();
/* cls1.add(12);
cls1.add(99.99);
cls1.add(false);
cls1.add('a');*/
cls1.add("Java EE");
cls1.add("Java EE");
// Collection<int> clss = new ArrayList<int>(); // 报错!
Collection<Integer> clss = new ArrayList<Integer>();// 正确
// 从JDK 1.7之后泛型后面的类型申明可以省略。
Collection<Integer> clss1 = new ArrayList<>();// 正确
}
}
集合使用泛型的好处:
- 1.如果集合不使用泛型,假如要做类型转换可能出现ClassCastException类型转换异常。
- 2.如果集合使用了泛型以后:集合的元素类型就确定了,不存在转型问题。
- 3.使用泛型以后,开发中操作的数据类型就非常的具体,便于管理!
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
/*
// 如果集合不使用泛型
Collection cls = new ArrayList();
cls.add("Java EE");
cls.add(12);
cls.add(99.99);
cls.add(false);
cls.add('a');
// foreach
for(Object obj : cls){
String rs = (String) obj;
System.out.println(rs);
}*/
Collection<String> cls = new ArrayList<>();
cls.add("Java EE");
cls.add("Oracle");
for(String ele : cls){
System.out.println(ele);
}
}
}
1.使用泛型来存储自定义数据类型
- 目标:集合使用泛型来存储自定义数据类型;
- 1.存储字符串。
- 2.存储学生对象。
- 总结:存什么类型的数据,泛型就写什么类型。
测试类:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo01 {
public static void main(String[] args) {
//1.创建一个集合存储学生对象
Collection<Student> stus = new ArrayList<Student>();
// 创建学生对象
Student xg = new Student("徐干",21,'男');
Student xb = new Student("徐冰",22,'男');
Student csy = new Student("陈水勇",22,'男');
// 加入到集合中去
stus.add(xg);
stus.add(xb);
stus.add(csy);
// stus.add(12); // 报错!
System.out.println(stus);
}
}
Student类:
public class Student {
private String name ;
private int age;
private char sex;
//以下代码省略
//有参满参构造器
//set,get方法
//toString方法
}
2.自定义泛型类
-
自定义泛型类:
- 我们用的泛型都是sun公司自己写好的,我们是否可以自己定义一个泛型类使用呢?
- 一个类如果使用了泛型那么这个类就是泛型类。
-
泛型类的格式:
修饰符 class 类名<泛型变量>{ ***** }
-
泛型变量一般选择: E T K V
- 一般是一个大写英文字母。
-
定义需求和步骤:
- 模拟sun公司定义一个ArrayList集合的泛型类。
- 1.定义一个泛型类叫MyArrayList
- 2.模拟两个方法 一个是add 一个是remove
-
总结:
- 其实泛型的核心就是在使用泛型的时候传入了具体的数据类型给泛型变量。
- 然后所有出现泛型变量的地方会被全部替换成具体的数据类型。
测试类:
import java.util.ArrayList;
public class FanXingDemo01 {
public static void main(String[] args) {
ArrayList<String> lists1 = new ArrayList<String>();
//ArrayList<String> lists2 = new ArrayList<>();
// 自己写的泛型类!
//MyArrayList<String> lists3 = new MyArrayList<String>();
MyArrayList<String> lists4 = new MyArrayList<>();
lists4.add("aaa");
MyArrayList<Integer> lists5 = new MyArrayList<>();
lists5.add(12);
//lists5.add("ss");
MyArrayList<Boolean> lists6 = new MyArrayList<>();
lists6.add(false);
lists6.add(true);
lists6.add(true);
lists6.add(true);
lists6.add(true);
lists6.remove(false);
System.out.println(lists6);
}
}
自己写的泛型类MyArrayList:
import java.util.ArrayList;
// 泛型类:
public class MyArrayList<E> {
// 1.创建一个ArrayList集合存储元素
public ArrayList lists = new ArrayList();
// 模拟一个添加方法
public boolean add(E e){
lists.add(e);
return true;
}
// 模拟一个方法
public void remove(E e){
lists.remove(e);
}
@Override
public String toString() {
return lists.toString();
}
}
3.泛型方法的定义
- 泛型方法:
- 一个方法如果使用了泛型那就是泛型方法。
- 泛型方法的格式:
修饰符 <泛型变量> 返回值类型 方法名称(形参-支持泛型){
}
-
<泛型变量>:无实际含义,只是约束方法只能用哪个泛型变量了。
-
如果是方法中就只能用泛型变量T
-
需求:
- 随便给你一个数组,请把这个数组的内容进行格式化输出
-
步骤:
- (1)定义一个数组,可能是int类型的,也可能是String,可能是double类型的数组。
- (2)定义一个泛型方法来接收这个数组
- (3)在方法中遍历这个数组然后输出成:[元素1,元素2, …]
-
总结:
- 使用了泛型的方法可以实现接收任意类型的数据。
- 一旦可以接收任意类型的数据,这个方法的功能就显得非常强大和通用!
import java.util.Arrays;
public class FanXingDemo01 {
public static void main(String[] args) {
Integer[] arrs = new Integer[]{10,40,80,89};
toString(arrs);
Double[] db = new Double[]{ 122.2 , 99.9 ,100.0};
toString(db);
Object[] objs = new Object[2];
toString(objs);
String[] names = new String[]{"徐干","老毛"};
toString(names);
}
// 通用:可以接收任意类型的数组。
public static <T> void toString(T[] arrs){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(T ele : arrs){
sb.append(ele+",");
}
sb.deleteCharAt(sb.length()-1); // 删除最后一个位置的“,”
sb.append("]");
System.out.println(sb);
}
}
4.泛型接口的定义
-
泛型接口:
- 一个接口如果使用了泛型就是泛型接口。
-
需求:
- 我现在需要做哪一个教务管理系统。
- 这个系统需要约束一定要有存储数据和删除数据的功能。
- 可能存储学生对象,可以存储老师对象,存储班级对象。
-
总结:
- 接口定义泛型,然后实现类实现接口的实现传输具体的数据类型即可。
SaveData接口:
public interface SaveData<E> {
// 存储数据
void save(E ele);
void delete(E ele);
}
SaveData接口实现:
public class SaveStudent implements SaveData<Student> {
@Override
public void save(Student ele) {
System.out.println("保存学生!");
}
@Override
public void delete(Student ele) {
System.out.println("删除学生!");
}
}
public class SaveTeacher implements SaveData<Teacher> {
@Override
public void save(Teacher ele) {
System.out.println("保存老师");
}
@Override
public void delete(Teacher ele) {
System.out.println("删除老师");
}
}
Student和Teacher类:
public class Student {
}
public class Teacher {
}
测试类:
public class TestMainCeshi {
public static void main(String[] args) {
SaveTeacher xulei = new SaveTeacher();
xulei.save(new Teacher());
xulei.delete(new Teacher());
SaveStudent xg = new SaveStudent();
xg.save(new Student());
xg.delete(new Student());
}
}
5.泛型的通配符:
场景实例:
-
如果一个方法需要接收一个类似于ArrayList这样的集合,那么这个方法可能不清楚这个ArrayList集合中的具体数据类型。但是这个方法又想可以接收任意的ArrayList集合这个时候就必须选择使用通配符“?”
- 通配符:? 在使用泛型的时候使用。可以代表一切类型。
- 泛型变量:在定义泛型的时候使用。可以接收一切类型。
-
注意:泛型是没有继承关系的,BMW虽然是Car的子类;但是ArrayList并不是ArrayList的子类。
-
需求: 极品飞车的游戏。
- 步骤:
- 1.定义一个Car类来表示汽车
- 2.定义一些汽车品牌表示不同的车BMW , JEEP继承自 Car.
- 3.定义一个比赛的方法,接收一批车来比赛。
- 在使用通配符的时候出现了业务问题,?代表一切类型,那么狗也可能进来比赛的这是不被允许的。
- 所以Java设计了一个所谓的泛型上下限来约束 ?的使用。
- 步骤:
-
泛型的上限: ? extends Car (?的类型必须是继承自Car类的子类或者就是Car类本身的对象)
-
泛型的下限: ? super Car (?的类型必须是Car类或者是Car类的父类对象)
public class Car {
}
public class BMW extends Car {
}
public class JEEP extends Car {
}
public class Dog {
}
测试类:
import java.util.ArrayList;
public class CollectionDemo01 {
public static void main(String[] args) {
ArrayList<JEEP> jeeps = new ArrayList<>();
jeeps.add(new JEEP());
jeeps.add(new JEEP());
jeeps.add(new JEEP());
comptition(jeeps);
ArrayList<BMW> bmws = new ArrayList<>();
bmws.add(new BMW());
bmws.add(new BMW());
bmws.add(new BMW());
comptition(bmws);
// 狗被排除了!
ArrayList<Dog> dogs = new ArrayList<>();
dogs.add(new Dog());
dogs.add(new Dog());
dogs.add(new Dog());
// comptition(dogs);
}
public static void comptition(ArrayList<? extends Car> jeeps){
}
}
六、斗地主发牌例子
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* 案例:
* 业务需求:
* 按照斗地主的规则,完成洗牌发牌的动作。
具体规则:
使用54张牌打乱顺序,三个玩家参与游戏,三人交替摸牌,
每人17张牌,最后三张留作底牌。
牌数
点数:"3" , "4" , "5" , "6" , "7" , "8", "9" , "10" , "J" , "Q" , "K", "A","2"
花色: "♥" , "♦" , "♠" , "♣"
从 3 -> 2 每个点数是有4张牌。与花色进行组合。
大小王各一张: 小☠ 大☠
开发步骤:
1.做出54张牌,用一个ArrayList集合存储起来。
2.把做好的牌打乱顺序。
3.定义三个玩家。
4.把54张牌依次发出51张给三个玩家。
5.输出三个玩家的牌和最后剩余的三张底牌!
总结:
1.要学会使用数组和集合。
2.注意开发的思路。
*/
public class DouDiZhuDemo01 {
public static void main(String[] args) {
// 1.做出54张牌,用一个ArrayList集合存储起来。
ArrayList<String> cards = new ArrayList<>();
// 定义点数:点数类型确定,大小也确定,无需增删,用数组定义即可,
String[] dss = new String[]{"3" , "4" , "5" , "6" , "7" , "8", "9" , "10" , "J" , "Q" , "K", "A","2"};
// 定义花色:四种,大小也确定,类型也确定,无需增删,用数组定义即可!
String[] colors = {"♥" , "♦" , "♠" , "♣"};
// 开始组合花色和点数做牌!!
for(String ds : dss){
for(String color : colors){
cards.add(color+ds);
}
}
// 加入大小王
cards.add("小☠");
cards.add("大☠");
System.out.println("新牌:"+cards);
/** 洗牌: 2.把做好的牌打乱顺序。 */
Collections.shuffle(cards);// 打乱List系列集合的顺序
System.out.println("洗牌后:"+cards);
/** 定义三个玩家:3.定义三个玩家。 */
ArrayList<String> linhuchong = new ArrayList<>();
ArrayList<String> qiaoFeng = new ArrayList<>();
ArrayList<String> yangguo = new ArrayList<>();
/** 发牌 : 把54张牌依次发出51张给三个玩家。
* cards =[♠8, ♦4, ♥Q, ♥5, ♥7, ♦2, ♦K, ♣2, ....
* 0 1 2 3 4 5 6 7 % 3
* 0 1 2 0 1 2 0 1
*/
for(int i = 0 ; i < cards.size() - 3 ; i++) {
// 取出当前的这张牌。
String card = cards.get(i);
// 开始判断这张牌应该给谁?
if(i % 3 == 0){
// 发给令狐冲
linhuchong.add(card);
}else if(i % 3 == 1){
// 发给乔峰
qiaoFeng.add(card);
}else if(i % 3 == 2){
// 发给杨过
yangguo.add(card);
}
}
// 最后三张底牌在cards集合的最后三张。因为只发出了51张牌。
// 从cards集合中截取最后三张底牌,返回的是一个新的集合,里面就是三张底牌!
List<String> lastThree = cards.subList(cards.size()-3,cards.size());
System.out.println("底牌:"+lastThree);
System.out.println("令狐冲:"+linhuchong);
System.out.println("乔峰:"+qiaoFeng);
System.out.println("过儿:"+yangguo);
}
}