泛型
1.泛型的概念:
泛型: 标识着 集合中存储元素的数据类型
写法: <数据类型(即泛型)>
// 泛型的写法:
// 创建一个集合 ,保存a b c d
// E 泛型 Element(元素)
// 注意:前后泛型的类型要保持一致(如果后面要填的话)
// jdk1.7 菱形泛型
// 后面泛型可以不填 默认和前面的一致
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
// 利用迭代器遍历
// 迭代器泛型 表示集合中保存的元素类型 [与实现类中的泛型保持一致]
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
String next = iterator.next();
System.out.println(next);
// 如果是自己写的类的对象,需要重写toString()
}
2.泛型的好处
1️⃣保证了数据的安全性 (提示你 方法中要传入的数据类型)
2️⃣避免了 向下转型(类型强转) [提示你 创建什么类型的对象接受迭代器传来的数据]
3️⃣将运行时的错误 转化成了编译错误 [类型接受错误时,不需要运行报错,直接提示]
// 创建集合 保存3学生
// 获取集合中的第0个学生 并打印姓名
ArrayList<Student> list = new ArrayList<>();
list.add(new Student("东东", 16)); // 好处 1️⃣:提示你方法中传入学生类对象
list.add(new Student("生生", 18));
list.add(new Student("柳柳", 20));
Object object = list.get(0);
Student student = (Student) object; // 好处 2️⃣:可以不用向下转型,直接用学生类对象接受
// 好处 3️⃣:若接受对象不对,不用运行才报错,直接报错(一般在迭代器中比较明显)
3.泛型的声明
1️⃣声明位置: 类名(接口名)<泛型>
假如:Student<S> 此时S是占位符
注意: 泛型使用占位符(英文字符)时 一般使用大写字母
2️⃣什么时候 指定泛型的真正类型?
创建对象时 会给泛型 赋值数据(对象)类型
// 举例:泛型的声明 与 指定数据类型
// 泛型的声明位置 (Worker类中属性的数据类型都为W)
class Worker<W>{
// 利用泛型 声明成员变量
private W w;
// 声明set/get方法
public void setW(W w) {
this.w = w;
}
public W getW() {
return w;
}
// 成员方法
public void fun(W w) {
System.out.println(w);
System.out.println("我是fun方法");
}
// 一个类中 可不可以有多个泛型? 可以
// 需要在方法上 进行泛型的声明
// 这个泛型 将会在 该方法被调用的时候 被传参(不一定是该类对象的属性)赋值
public<Y> void fun1(Y y) {
System.out.println(y);
}
// 静态方法 能不能使用 W 泛型 ? 不能
// 当方法被调用时,泛型被赋值
public static<Q> void fun2(Q q) {
System.out.println(q);
}
}
// 指定泛型的真正数据类型
// 此时泛型占位符W 被赋值 String
Worker<String> worker = new Worker<>();
worker.setW("wangjun");
System.out.println(worker.getW());
worker.fun("少年强则中国强");
worker.fun1('1'); // 传入的参数是字符,该Y被赋值char
// 接口中的泛型
// 泛型接口
interface InterA<G>{
public abstract void fun(G g);
}
// 泛型类型确定 可以在 接口的实现类上确定
class InterAImpl implements InterA<String>{
@Override
public void fun(String g) {
// TODO Auto-generated method stub
}
}
多参数方法(int … num)
int ... num (输入int类型 从0到num)
可以接受多个 int类型值 相当于参数是一个数组[0,1,...num]
调用方式两种
1.传入多个值 用逗号隔开
2.直接传入数组
// 第一种方法(传入多个值,逗号隔开)调用
注意:可以添加不同类型的参数,但是要放在前面,放在后面会报错
(换句话说: int ... num 要放在最后面)
public static void print(String str,int ... num) {
// 遍历(获取你传进来有几个int值)
for (int i = 0; i < num.length; i++) {
System.out.println(num[i]);
}
}
// 第二种方法(输入数组)隔开
int[] arr = {2,3,4,5};
print(" ",arr);
数组转集合:Arrays.asList(数组名),记住用集合接收
// ❎ 错误的 原因:集合中只能放引用类型的数据,不能放基本数据类型
int[] array = {1,2,3,4,5};
List<int[]> list = Arrays.asList(array);
System.out.println(list);
// ✔️ 正确的 装箱了
Integer[] array1 = {1,2,3,4,5};
List<Integer> list1 = Arrays.asList(array1);
System.out.println(list1);
// 创建一个字符串数组 保存三个名字
// 将数组 转成集合
// 添加一个名字
String[] arr = {"东东","生生","柳柳"};
// 将数组 转成 集合
List<String> list2 = Arrays.asList(arr);
System.out.println(list2);
// 添加名字
// UnsupportedOperationException
// 不支持操作异常
// 注意:该方法 转完集合后 不支持对集合的长度修改
// list.add("花花");
// 该方法意义在于: 可以使用集合类中 其他方法
boolean b = list2.contains("生生");
System.out.println(b);
4.泛型在继承中的应用: 将子类或本类放入集合中
// 前提: class Student extends Person
// 举例:
ArrayList<Person> list = new ArrayList<Person>();
list.add(new Student()); // 添加子类
list.add(new Person()); // 添加本类
去重删除
一般两种方法 1.for循环删除 2.迭代器删除
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("b");
list.add("c");
list.add("d"); [a,b,b,c,d] 删除两个b
// 1.for循环删除
for (int i = 0; i < list.size(); i++) {
String string = list.get(i);
if (string.equals("b")) {
list.remove(i--);
// 删除一个,其他的前移一位,若两个连续,则只能删除一个
// --,是为了循环在再进行一次,删除下一个b
}
}
System.out.println(list);
// 2.迭代器删除
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if (str.equals("b")) {
iterator.remove();
}
}
System.out.println(list);
集合工具类:Collection.swap(list,i,j) 交换位置
Set接口-HashSet实现类
1.HashSet实现类的特点: 无序 无角标 不可重复(继承Set接口)
自带去重 (前提:对象地址重复,才能去重,不能根据属性直接去重)
2.HashSet集合的建立
// 创建Set集合 添加a a b b c c
// 有序: 指的是存取的顺序 存的顺序就是取的顺序
// 迭代器遍历
HashSet<String> set = new HashSet<>();
set.add("f");
set.add("a");
set.add("a");
set.add("b");
set.add("b");
set.add("c");
set.add("c");
// 迭代器遍历
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
String string = (String) iterator.next();
System.out.println(string);
}
结果:
a,b,c,f(无序)
3.Set的自动去重
HashSet<Man> set = new HashSet<>();
Man m1 = new Man("1",11);
Man m2 = new Man("1",11);
Man m3 = new Man("2",22);
Man m4 = new Man("2",22);
Man m5 = new Man("3",33);
Man m6 = new Man("3",33);
set.add(m1);
set.add(m2);
set.add(m3);
set.add(m4);
set.add(m5);
set.add(m6);
Iterator<Man> iterator = set.iterator();
while (iterator.hasNext()) {
Man m = (Man) iterator.next();
System.out.println(m);
}
直接输出结果: 一个没删
没去重原因:
// 系统每创建一个对象 都会为这个对象
// 分配一个 hashCode值
// 当向HashSet集合中 保存对象时
// 系统会先比较hashCode值是否相同
// 相同: 再调用对象的equals方法进行比较,
// 如果equals方法比较也相同,那么就不存
// 反之,会存储到集合中
// 不相同: 如果HashCode不相同,就相当于不是一个对象
// 那么直接就把该对象 直接存入集合中
// 也不会调用equals方法
解决办法:
// 重写hashCode和equals方法
// 举例
/*
* 随机10个数 [10,20] 装到集合中去重
* Integer可以直接去重
* String可以直接去重
* 系统已经重写好了hashCode()方法和equals()方法
* 自己写的类没有写好,所以自己要重写
*/
HashSet<Integer> set = new HashSet<>();
for (int i = 0; i < 10; i++) {
int num = (int)(Math.random() * 11 + 10);
set.add(num);// 自动装箱
}
System.out.println(set);