一.泛型
1.泛型的概述
- 泛型:广泛的类型,定义一个类型,类中方法参数或返回值,不能确定类型,那么可以泛型表示。
- 格式:在类型的后面,使用<>,在<>中写上一个大写的英文字母,可以是E,K,V,T,W,Q。
- 优点:
(2)在进行元素的获取时,不需要再进行转型
- 注意事项
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class FanXing {
public static void main(String[] args) {
//notFanXingMethod();
fanXingMethod();
}
public static void notFanXingMethod(){
//三种方法创建集合
/*1.Collection coll = new ArrayList();
2.List list1 = new ArrayList();*/
ArrayList list = new ArrayList();/*3.没有泛型时*/
list.add("a");
list.add("b");
list.add(1);//整形不能转为字符串 class java.lang.Integer cannot be cast to class java.lang.String
for(int i = 0;i<list.size();i++){
Object obj = list.get(i);
String s = (String)obj;//向下转型报错
System.out.println(s);
}
}
public static void fanXingMethod(){
//定义一个带有泛型的集合,该集合将来存储的是String类型
ArrayList<String> list = new ArrayList<>();
/*list.add(1);存储报错,不能存储整形*/
list.add("1");
list.add("2");
list.add("abc");
for (int i = 0; i < list.size(); i++) {
String s= list.get(i);
System.out.println(s);
}
}
}2.带有泛型的类
格式:
修饰符 class 类名<泛型1,泛型2...> {
}
public class ArrayList<E>- 说明:
2.泛型可以作为一个已知的类型,在整个类中使用(类上的泛型可当做类的成员变量使用)
3.当创建一个类对象时,确定泛型具体类型
3.带有泛型的方法
格式:
修饰符<泛型1,泛型2...> 返回值类型 方法名(参数列表){
retrun;
}- 说明:
2.静态方法,如果要使用泛型,只能在方法上,自己定义泛型。
原因:静态属于类,静态优先于对象存在的,静态方法不能使用类的泛型,类上的泛型,只有在创建对象时,才能确定具体类型。
3.方法上定义的泛型只能在方法中使用
import java.util.ArrayList;
import java.util.Arrays;
public class FanXingClass<E> {
//定义一个带有泛型的类,泛型定义为E,E表示一个广泛类型
//1.泛型可以在整个类中作为一个已知类使用(可以当成成员变量使用)
//使用泛型E
ArrayList<E> al = new ArrayList<>();
//方法中使用类的泛型
public void addE(E e){
al.add(e);
System.out.println(al.get(0));//12
}
//静态方法只能使用自己定义的泛型,无法使用类上的泛型
//方法功能:替换arr数组中a和b两个索引的元素
public static <E> void changeE(E[] arr,int a,int b ){
E e = arr[a];
arr[a] = arr[b];
arr[b] = e;
}
public static void main(String[] args){
//2.创建类的同时,确定泛型的具体类型
//FanXingClass<Integer> --> 泛型E就表示Integer类型
FanXingClass<Integer> fx = new FanXingClass();
// 3.调用带有泛型的方法,需要传递实际参数,参数类型E-->Integer类型
fx.addE(12);
//4.调用静态的带有泛型的方法
Integer[] i = {12,13,14,15};
int a = 1;
int b = 2;
FanXingClass.changeE(i,a,b);
System.out.println( Arrays.toString(i));//[12, 14, 13, 15]
}
}4.带有泛型的接口
格式:
修饰符 interface 接口名<泛型1,泛型2...>{
//抽象方法
}- 说明: 带有泛型的接口,实现类情况
2.实现类带有泛型,那么实现的接口就不需要给出具体泛型类型
3.接口上的泛型可以在整个接口中,当做已知类使用
举例:Collection<E> --> LIst<E> --> ArrayList<E>
//定义一个带有泛型的接口
//关注的是带有泛型接口的实现类
public interface MyInterface<E> {
public abstract void get(E e);//方法参数使用泛型
public abstract E fun();//方法返回值使用泛型
}
//1.定义一个不带有泛型的实现类,要求接口的泛型必须确定
class MyInterfaceImpl1 implements MyInterface <String>{
@Override//重写的方法已经将所有泛型替换为String了
public void get(String s) {}
@Override
public String fun() { return null; }
}
//2.定义一个带有泛型的实现类
class MyInterfaceImpl2<E> implements MyInterface<E>{//将来创建类对象时确定具体的泛型类型
@Override
public void get(E e) { }
@Override
public E fun() { return null; }
}5.泛型的通配符和泛型擦除
?:表示泛型的通配符,可以匹配任意的泛型类型
1.containsAll(Collection<?> c):匹配任意类型的集合
2.addAll(Collection<? extends E> c):方法调用集合的类型作为E存在,参数的集合形式,只能是方法调用集合的子类或者是本类类型
3.TreeSet(Comparator<? super E> compartor):需要传递的参数是E类型本身,或者是E的父类
泛型擦除:
进行代码编写时,可以添加泛型,加了泛型时为了提高代码的安全性
当源文件编译成.class文件时,泛型是不存在的,.class文件中没有泛型的
import java.util.ArrayList;
public class FanXingTongPei {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<>();
list1.add("a");
list1.add("b");
ArrayList<Integer> list2 = new ArrayList<>();
list2.add(1);
list2.add(2);
System.out.println(list1.containsAll(list2));//false
//addAll(Collection<? extends E>)
//E的泛型类型,来自于list1集合中的泛型String
//? extends E --> ? extends String 表示追加的集合中的元素,只能是String类型本身或者是String的子类
ArrayList<Object> list3 = new ArrayList<>();
//list1.addAll(list2);//list2并不是String类型,报错
list3.addAll(list1);
}
}二.Set集合
1.Set集合的概述
- List集合特点:有序、有索引、可重复
- Set集合,是接口,来自于java.util.包
- 接口不能实例化对象,于是找到了一个实现类HashSet
- Set集合特点:无序、无索引、不可重复
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
//创建一个Set集合容器
Set<String> set = new HashSet<>();
//向容器中添加元素
set.add("c");
set.add("a");
set.add("hello");
set.add("a");
set.add("c");
set.add("A");
System.out.println(set);//[a, A,c, hello]
}
}2.Set集合的遍历
将Set集合中的元素一个一个的获取
1.toArray():将Set集合中的内容,放置到一个Object[]中,然后通过遍历数组,就可以实现遍历Set集合中的元素;
2.toArray(T[] t):返回一个包含Set集合元素的T类型的数组t;
3.iterator():迭代器,返回的类型iterator<>,迭代器中的泛型与集合中的泛型保持一致
4.增强for,forEach:
for(数据类型 变量名:集合或数组){
}
(1)集合或数组表示需要进行遍历的容器
(2)数据类型表示集合或者数组中的元素类型
(3)变量名:表示从集合或数组中获取到的每一个元素的表示
注意:
增强for通过集合或数组中是否具有下一个元素进行遍历的,与容器是否具有索引,无关
增强for底层是迭代器,因此在使用增强for进行集合遍历时,如果同时向集合中添加元素,报出并发修改异常
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetBianLi {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(9);
set.add(-2);
set.add(13);
set.add(29);
f1(set);
f2(set);
f3(set);
f4(set);
}
//1.toArray():
public static void f1(Set<Integer> set){
Object[] obj = set.toArray();
for (int i = 0; i < obj.length; i++) {
Integer a = (Integer) obj[i];
System.out.print(a+" ");//-2 9 13 29
}
System.out.println("====================");
}
//2.toArray(T[] t)
public static void f2(Set<Integer> set){
//准备一个大小与set集合一致的Integer类型的数组,将集合中的元素,放置到Integer类型的数组中
Integer[] it = new Integer[set.size()];
set.toArray(it);//it为Integer类型属于泛型,返回一个Integer类型的数组it
for (int i = 0; i < it.length; i++) {
System.out.print(it[i]+" ");//-2 9 13 29
}
}
//3.iterator():
public static void f3(Set<Integer> set){
Iterator it = set.iterator();
while(it.hasNext()){
System.out.print(it.next()+" ");//-2 9 13 29
}
}
//4.增强for
public static void f4(Set<Integer> set){
for(Integer a : set){
System.out.print(a+" ");//-2 9 13 29
}
}
}练习:随机生成10个1-100的随机数,存储在合适的集合中,并且遍历集合
要求:集合中存储的随机数不重复import java.util.HashSet;
import java.util.Random;
import java.util.Set;
public class SetTest {
public static void main(String[] args) {
//1.创建一个随机数对象
Random r= new Random();
//2.生成1-100的随机数
//3.放入Set集合中
Set<Integer> set = new HashSet<>();
for (int i = 0; i < 10; i++) {////while(set.size()<10){}
set.add( r.nextInt(99)+1);
}
//4.遍历
for(Integer a : set){
System.out.print(a+" ");//65 7 8 10 12 29 94 78 31
}
}
}3.Set集合保证元素唯一
1.Set集合存储JDK定义好的引用数据类型
原理:因为JDK中定义好的引用数据类型,都重写了Object中的hashCode和equals方法。
2.Set集合存储自定义引用数据类型
如果Set集合中存储的引用数据类型需要去重复,那么将hashcode()和equals()自动重写
分析:
(1)Object 源代码:
hashcode():将对象在内存中的地址值,转化成一个int类型的整数
equals():比较的是对象的地址值是否相等
(2)重写后的方法:
hashcode():将对象的成员变量获取到,将所有成员所对应的哈希值计算出来
equals():比较的是成员变量的值是否相等
3.Set集合去重复原理
1.先将对象的hashCode值计算出来,判断set集合中,是否有相同的哈希值,如果没有,直接添加元素成功。
2.如果添加的对象的哈希值与set集合中其他元素的哈希值相同,然后判断相同哈希值的equals方法的比较结果
(1)equals()比较,判断两个对象的成员变量值是否完全相同,如果完全一致,重复,添加失败。
(2)equals()比较,判断两个对象的成员变量值是否不完全相同,内容不一致,添加set集合成功import java.util.HashSet;
public class SetOnly {
public static void main(String[] args) {
fun();
fun1();
}
//Set集合存储JDK定义好的应用数据类型,去重复
public static void fun(){
HashSet<String> set1 = new HashSet<>();
set1.add("a");
set1.add("a");
set1.add("b");
System.out.println(set1);//[a, b]
HashSet<Integer> set2 = new HashSet<>();
set2.add(12);
set2.add(25);
set2.add(12);
set2.add(25);
System.out.println(set2);//[25, 12]
}
//Set集合存储自定义的Person类型
public static void fun1(){
HashSet<Person> set1 = new HashSet<>();
Person p = new Person("张三",20);
set1.add(p);
set1.add(new Person("李四",23));
set1.add(new Person("张三",20));
set1.add(new Person("李四",23));
//System.out.println(set1);//[com.itcast.bjzhang.demo04.Person@1d81eb93, com.itcast.bjzhang.demo04.Person@2f4d3709, com.itcast.bjzhang.demo04.Person@b4c966a, com.itcast.bjzhang.demo04.Person@4e50df2e]
//如果不想打印地址,Person类重写toString()
//System.out.println(set1);//[Person{name='李四', age=23}, Person{name='李四', age=23}, Person{name='张三', age=20}, Person{name='张三', age=20}]
//没有去重,若想去重复,Person类重写hashcode() and equals()方法
System.out.println(set1);//[Person{name='张三', age=20}, Person{name='李四', age=23}]
}
}
import java.util.Objects;
public class Person {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person person = (Person) o;
return getAge() == person.getAge() &&
Objects.equals(getName(), person.getName());
}
@Override
public int hashCode() {
return Objects.hash(getName(), getAge());
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}4.LinkedHashSet
- LinkedHashSet是hashSet的子类,LinkedHashSet存储元素有序
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet();
set.add("c");
set.add("a");
set.add("hello");
set.add("a");
set.add("c");
set.add("A");
set.add(null);
System.out.println(set);//[c, a, hello, A, null]
}
}