1、泛型
1.1 泛型概述
在前面学习集合时,我们都知道集合中是可以存放任意对象的,只要把对象存储集合后,那么这时他们都会被提升成Object类型。当我们在取出每一个对象,并且进行相应的操作,这时必须采用类型转换。
package com.suyv.genericity;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 12:26
*@Description: TODO
*/
public class GenericityDemo {public static void main(String[] args) {Collection coll = new ArrayList();coll.add("abc");coll.add("cba");coll.add(5);//由于集合没有做任何限定,任何类型都可以给其中存放Iterator it = coll.iterator();while(it.hasNext()){//需要打印每个字符串的长度,就要把迭代出来的对象转成String类型String str = (String) it.next();System.out.println(str.length());}}
}
程序在运行时发生了问题java.lang.ClassCastException。为什么会发生类型转换异常呢?
我们来分析下:由于集合中什么类型的元素都可以存储。导致取出时强转引发运行时 ClassCastException
。
怎么来解决这个问题呢?
- 集合中可以存储各种对象,但实际上通常集合只存储同一类型对象。例如都是存储字符串对象。因此在JDK5之后,新增了泛型(Generic)语法,让你在设计API时可以指定类或方法支持泛型,这样我们使用API的时候也变得更为简洁,并得到了编译时期的语法检查。
- 泛型:可以在类或方法中预支地使用未知的类型。
1.2 使用泛型的好处
泛型带来了哪些好处呢?
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
- 避免了类型强转的麻烦。
通过我们如下代码体验一下:
package com.suyv.genericity;import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 12:28
*@Description: TODO
*/
public class GenericityDemo01 {public static void main(String[] args) {Collection<String> list = new ArrayList<String>();list.add("abc");list.add("cba");// list.add(5); // 当集合明确类型后,存放类型不一致就会编译报错// 集合已经明确具体存放的元素类型,那么在使用迭代器的时候,迭代器也同样会知道具体遍历元素类型Iterator<String> it = list.iterator();while(it.hasNext()){String str = it.next();// 当使用Iterator<String>控制元素类型后,就不需要强转了。获取到的元素直接就是String类型System.out.println(str.length());}}
}
1.3 泛型的定义与使用
泛型:是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。
我们在集合中会大量使用到泛型,这里来完整地学习泛型知识。泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
通常情况下,T,E,K,V,?是这样约定的:
- ?表示不确定的 java 类型
- T (type) 表示具体的一个java类型
- K V (key value) 分别代表java键值中的Key Value
- E (element) 代表Element
1.3.1 泛型类
定义格式
- <数据类型> 此处的数据类型只能是引用类型。
好处:
- 把运行时期的问题提前到了编译期间
- 避免了强制类型转换
自定义泛型类
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 12:29
*@Description: 泛型类:把泛型定义在类上
*/
public class ObjectTool <T>{private T obj;public T getObj() {return obj;}public void setObj(T obj) {this.obj = obj;}}
如何使用泛型类
使用泛型: 即什么时候确定泛型。在创建对象的时候确定泛型
package com.suyv.genericity;import org.junit.Test;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 12:31
*@Description: 泛型类的测试
*/
public class GenericityDemo02 {@Testpublic void Test01(){ObjectTool<String> ot = new ObjectTool<String>();ot.setObj(new String("江一燕"));String s = ot.getObj();System.out.println("姓名是:" + s); // 姓名是:江一燕ObjectTool<Integer> ot2 = new ObjectTool<Integer>();ot2.setObj(27);Integer i = ot2.getObj();System.out.println("年龄是:" + i); // 年龄是:27}
}
1.3.2 泛型方法
定义格式
- 修饰符 <代表泛型的变量> 返回值类型 方法名 (参数){ }
定义泛型方法
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:09
*@Description: 泛型方法:把泛型定义在方法上
*/
public class ObjectTool01 {//定义一个泛型方法//返回类型不应该明确,因为泛型方法的类型都不明确//建议: Object 或者 T(泛型)public <T> T show(T t){System.out.println(t);return t;}}
泛型方法的使用
调用方法时,确定泛型的类型
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:10
*@Description: 泛型方法测试
*/
public class GenericityDemo03 {public static void main(String[] args) {// 定义泛型方法后ObjectTool01 ot = new ObjectTool01();String s = (String) ot.show("hello");Integer i = (Integer) ot.show(200);Boolean b = (Boolean) ot.show(true);System.out.println(s);System.out.println(i);System.out.println(b);}
}
1.3.3 泛型接口
定义格式
- 修饰符 interface接口名<代表泛型的变量> { }
定义泛型接口
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:12
*@Description: 泛型接口:把泛型定义在接口上
*/
public interface Inter <T>{public void show(T t);
}
泛型接口的使用
实现类
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:13
*@Description: 实现类在实现接口的时候 第一种情况:已经知道该是什么类型的了
*/
public class InterImpl implements Inter<String>{@Overridepublic void show(String s) {System.out.println(s);}
}/***@Author: 憨憨浩浩*@CreateTime: 2023/12/19 13:15*@Description: 实现类在实现接口的时候 第二种情况:还不知道是什么类型的*/
class InterImpl01<T> implements Inter{@Overridepublic void show(Object o) {System.out.println(o);}
}
测试
package com.suyv.genericity;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:16
*@Description: 泛型接口测试
*/
public class GenericityDemo04 {public static void main(String[] args) {//第一种情况:实现类已经明确类型,实例化对象时必须与实现类中的类型一致InterImpl i = new InterImpl();//我在实现的时候,已经明确类型--Stringi.show("aaa");i.show("bbb");//第二种情况:实现类也没有明确类型InterImpl01<Integer> ii = new InterImpl01<>();//我在实现的时间也没有给出明确ii.show(11);ii.show(22);InterImpl01<String> ii2 = new InterImpl01();//我在实现的时间也没有给出明确ii2.show("11");ii2.show("22");}
}
1.4 泛型通配符-了解
1.4.1 什么是泛型通配符
当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符<?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。
1.4.2 通配符基本使用
泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。
此时只能接受数据,不能往该集合中存储数据。
1.4.3 通配符高级使用
package com.suyv.genericity;import java.util.ArrayList;
import java.util.Collection;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:21
*@Description: TODO
*/
public class GenericityDemo05 {public static void main(String[] args) {// 泛型如果明确的写的时候,前后必须一致Collection<Object> c1 = new ArrayList<Object>();// Collection<Object> c2 = new ArrayList<Animal>();// Collection<Object> c3 = new ArrayList<Dog>();// Collection<Object> c4 = new ArrayList<Cat>();// ?表示任意的类型都是可以的Collection<?> c5 = new ArrayList<Object>();Collection<?> c6 = new ArrayList<Animal>();Collection<?> c7 = new ArrayList<Dog>();Collection<?> c8 = new ArrayList<Cat>();// ? extends E:向下限定,E及其子类,表示包括E在内的任何子类;// Collection<? extends Animal> c9 = new ArrayList<Object>();Collection<? extends Animal> c10 = new ArrayList<Animal>();Collection<? extends Animal> c11 = new ArrayList<Dog>();Collection<? extends Animal> c12 = new ArrayList<Cat>();// ? super E:向上限定,E极其父类,表示包括E在内的任何父类;Collection<? super Animal> c13 = new ArrayList<Object>();Collection<? super Animal> c14 = new ArrayList<Animal>();// Collection<? super Animal> c15 = new ArrayList<Dog>();// Collection<? super Animal> c16 = new ArrayList<Cat>();}
}class Animal {
}class Dog extends Animal {
}class Cat extends Animal {
}
2、Collections工具类
参考操作数组的工具类:Arrays,Collections 是一个操作 Set、List 和 Map 等集合的工具类。
2.1 常用方法
Collections 中提供了一系列静态方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法(均为static方法):
2.1.1 排序操作
reverse(List)
反转 List 中元素的顺序
shuffle(List)
对 List 集合元素进行随机排序
sort(List)
根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator)
根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int)
将指定 list 集合中的 i 处元素和 j 处元素进行交换
package com.suyv.collections;import org.junit.Test;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 14:59
*@Description: TODO
*/
public class CollectionsDemo01 {// reverse(List):反转 List 中元素的顺序@Testpublic void Test01(){List list = new ArrayList();list.add("11");list.add("22");list.add("33");list.add("44");list.add("55");System.out.println(list); // [11, 22, 33, 44, 55]// 反转list集合Collections.reverse(list);System.out.println(list); // [55, 44, 33, 22, 11]}// shuffle(List):对 List 集合元素进行随机排序@Testpublic void Test02(){List list = new ArrayList();list.add("11");list.add("22");list.add("33");list.add("44");list.add("55");System.out.println(list); // [11, 22, 33, 44, 55]// 随机排序list集合Collections.shuffle(list);System.out.println(list); // [22, 11, 33, 55, 44]}// sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序@Testpublic void Test03(){List list = new ArrayList();list.add("11");list.add("33");list.add("44");list.add("22");list.add("55");System.out.println(list); // [11, 33, 44, 22, 55]// 升序排序list集合--自然排序实现Comparable接口Collections.sort(list);System.out.println(list); // [11, 22, 33, 44, 55]}// sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序@Testpublic void Test04(){List list = new ArrayList();list.add("11");list.add("33");list.add("44");list.add("22");list.add("55");System.out.println(list); // [11, 33, 44, 22, 55]// 降序排序list集合--定制排序创建Comparator对象Collections.sort(list, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if(o1 instanceof String && o2 instanceof String){String u1 = (String) o1;String u2 = (String) o2;return -(u1.compareTo(u2));}return 0;}});System.out.println(list); // [55, 44, 33, 22, 11]}// swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换@Testpublic void Test05(){List list = new ArrayList();list.add("11");list.add("33");list.add("44");list.add("22");list.add("55");System.out.println(list); // [11, 33, 44, 22, 55]// 交换list集合的指定位置Collections.swap(list,1,4);System.out.println(list); // [11, 55, 44, 22, 33]}}
2.1.2 查找
Object max(Collection)
根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator)
根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,Comparator)
根据 Comparator 指定的顺序,返回给定集合中的最小元素
int binarySearch(List list,T key)
在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
int binarySearch(List list,T key,Comparator c)
在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且集合也事先必须是按照c比较器规则进行排序过的,否则结果不确定。
int frequency(Collection c,Object o)
返回指定集合中指定元素的出现次数
package com.suyv.collections;import org.junit.Test;import java.util.*;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 16:06
*@Description: TODO
*/
public class CollectionsDemo02 {// Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素// Object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素@Testpublic void Test01(){Set set = new HashSet();set.add("11");set.add("33");set.add("44");set.add("22");set.add("55");System.out.println(set); // [11, 33, 44, 22, 55]// 根据元素的自然顺序,返回给定集合中的最大元素System.out.println(Collections.max(set)); // 55}// Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素// Object min(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最小元素@Testpublic void Test02(){Set set = new HashSet();set.add("11");set.add("33");set.add("44");set.add("22");set.add("55");System.out.println(set); // [11, 33, 44, 22, 55]// 根据 Comparator 指定的顺序,返回给定集合中的最大元素Object max = Collections.max(set, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {if (o1 instanceof String && o2 instanceof String) {String u1 = (String) o1;String u2 = (String) o2;return -(u1.compareTo(u2));}return 0;}});System.out.println(max); // 11}// int binarySearch(List list,T key)在List集合中查找某个元素的下标,// 但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的。// 而且集合也事先必须是有序的,否则结果不确定。@Testpublic void Test03(){List list = new ArrayList();list.add("11");list.add("33");list.add("44");list.add("22");list.add("55");System.out.println(list); // [11, 33, 44, 22, 55]Collections.sort(list);System.out.println(list); // [11, 22, 33, 44, 55]int i = Collections.binarySearch(list, "33");System.out.println(i); // 2}// int binarySearch(List list,T key,Comparator c)在List集合中查找某个元素的下标,// 但是List的元素必须是T或T的子类对象,而且集合也事先必须是按照比较器规则进行排序过的,否则结果不确定。// int frequency(Collection c,Object o):返回指定集合中指定元素的出现次数@Testpublic void Test05(){List list = new ArrayList();list.add("11");list.add("33");list.add("33");list.add("44");list.add("22");list.add("33");list.add("55");System.out.println(list); // [11, 33, 33, 44, 22, 33, 55]System.out.println(Collections.frequency(list, "33")); // 3}
}
2.1.3 复制、替换
void copy(List dest,List src)
将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal)
使用新值替换 List 对象的所有旧值提供了多个unmodifiableXxx()方法,该方法返回指定 Xxx的不可修改的视图。
package com.suyv.collections;import org.junit.Test;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 16:21
*@Description: TODO
*/
public class CollectionsDemo03 {// void copy(List dest,List src):将src中的内容复制到dest中@Testpublic void Test01(){List list1 = new ArrayList<>();list1.add("11");list1.add("33");list1.add("44");list1.add("22");list1.add("55");List list2 = new ArrayList<>();list2.add("喜羊羊");list2.add("美羊羊");list2.add("懒羊羊");list2.add("暖羊羊");// list2.add("沸羊羊");Collections.copy(list1,list2);System.out.println(list1); // [喜羊羊, 美羊羊, 懒羊羊, 暖羊羊, 55]}// boolean replaceAll(List list,Object oldVal,Object newVal):// 将列表中一个指定值的所有出现替换为另一个。@Testpublic void Test02(){List list1 = new ArrayList<>();list1.add("11");list1.add("33");list1.add("33");list1.add("44");list1.add("22");list1.add("33");list1.add("55");System.out.println(list1); // [11, 33, 33, 44, 22, 33, 55]Collections.replaceAll(list1,"33","懒羊羊");System.out.println(list1); // [11, 懒羊羊, 懒羊羊, 44, 22, 懒羊羊, 55]}
}
2.1.4 添加
boolean addAll(Collection c,T... elements)
将所有指定元素添加到指定 collection 中。
package com.suyv.collections;import org.junit.Test;import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 13:26
*@Description: TODO
*/
public class CollectionsDemo04 {// boolean addAll(Collection c, T... elements)// 将所有指定元素添加到指定 collection 中。@Testpublic void Test01(){List<String> lists = new ArrayList();Collections.addAll(lists,"111","222","333","444");System.out.println(lists); // [111, 222, 333, 444]}
}
2.1.5 同步
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
2.2 collection和collectons的区别?
Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。
Collections是一个操作集合的工具类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
3、小案例--模拟斗地主
模拟斗地主洗牌和发牌并对牌进行排序的代码实现。
package com.work;import java.util.*;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-18 18:15
*@Description: 模拟斗地主
*/
public class Demo02 {// 创建扑克牌牌组private static Map cards = new HashMap();public static void main(String[] args) {// 定义花色String[] colors = {"♠", "♥", "♣", "♦"};// 定义数字String[] nums = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};// 定义集合接受牌组的序号List list = new ArrayList();// 牌组key值int key = 1;// 拼接花色和数字,构成牌组for (String num : nums) {for (String color : colors) {// 拼接花色和数字String card = num + color;// 将各个花色的牌加入牌组cards.put(key,card);list.add(key);key++;}}// 将大小王加入牌组cards.put(key,"小王");list.add(key);cards.put(key + 1,"大王");list.add(key + 1);// 洗牌,乱序Collections.shuffle(list);// 玩家1List player1 = new ArrayList();// 玩家2List player2 = new ArrayList();// 玩家3List player3 = new ArrayList();// 底牌List bottom = new ArrayList();// 发牌// 确定底牌bottom.add(list.get(0));bottom.add(list.get(1));bottom.add(list.get(2));// 给玩家发牌for (int i = 3; i < list.size(); i++) {if (i % 3 == 0){player1.add(list.get(i));} else if (i % 3 == 1) {player2.add(list.get(i));} else if (i % 3 == 2) {player3.add(list.get(i));}}// 玩家1牌组printCards("喜羊羊",player1);// 玩家2牌组printCards("懒羊羊",player2);// 玩家3牌组printCards("沸羊羊",player3);// 底牌牌组printCards("底 牌",bottom);}// 声明一个打印对应牌组的方法public static void printCards(String name, List list){// 对玩家手中牌进行排序Collections.sort(list);System.out.print(name + ":");// 打印玩家手中的牌组for (Object o : list) {int i = (Integer) o;System.out.print(cards.get(i) + " ");}System.out.println();}
}
补充:
补充1:静态导入
静态导入格式:
package com.suyv.collections;import java.util.ArrayList;
import java.util.List;import static java.util.Collections.*;/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 15:13
*@Description: 静态导入
*/
public class StaticImport {public static void main(String[] args) {List<String> lists = new ArrayList<>();// 添加元素addAll(lists,"张三","李四","王五","赵六","田七");// 乱序shuffle(lists);System.out.println(lists); // [李四, 张三, 王五, 赵六, 田七]}
}
补充2:可变参数
可变参数格式:
package com.suyv.collections;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 15:17
*@Description: TODO
*/
public class ArgsDemo<T> {public void show(T... mess){for (int i = 0; i < mess.length; i++) {System.out.println(mess[i]);}}
}
package com.suyv.collections;
/**
*@Author: 憨憨浩浩
*@CreateTime: 2023-12-19 15:17
*@Description: 可变参数
*/
public class ManyArgs {public static void main(String[] args) {ArgsDemo<String> arg = new ArgsDemo<>();arg.show("张三","李四","王五","赵六","田七");// 张三// 李四// 王五// 赵六// 田七}
}