面向对象-泛型
面向对象-泛型
没有泛型存在的问题
1.往集合当中存储元素,可以存储任何类型元素
List list = new ArrayList();
list.add(1);
list.add("a");
取出来的都是Object类型
Object obj = list.get(1);
现在想要使用字符串当中的特有的方法,必须得要转回来
String str= (String)obj;
str.substring(0, 1);
2.没有办法约束集合当中只能存储某一种类型
3.如:设计一个类 点(point x,y)
想要传入多个不一样类型,同样参数的,就需要用到泛型
class Point{
private String x;
private String y;
private Integer x1;
private Integer y1;
private Double x2;
private Double y2;
}
泛型定义
什么是泛型?
广泛通用的类型 (一开始还不清楚是什么类型,在使用的时候,才能确定是什么类型)
代码模块中类型不确定,谁调用该段代码,谁就可以来指明这个类型
定义时分为两步
1.在类的后面<> T:代表不确定的类型,在创建对象的时候,才确定是什么类型
T:type E:element K:key
class Point<T>{
1.可以是字符串类型
2.Integer
3.Double
2.在变量的前面添加上一个 T
把点定义为泛型
T x;
T y;
}
创建对象的时候并没有声明泛型是什么类型
如果没有指明泛型的类型,它就是Object类型,没使用泛型,什么类型都能传进去
Point<String> p = new Point<String>();
p.x="10";
p.y="20";
在创建对象指明泛型,这个类型可以是任意的类型(只能是引用类型)
Point<Integer> p2 = new Point<Integer>();
p2.x=10;
p2.y=20;
集合使用泛型的好处
规定集合当中只能存放String类型 (不能是基本数据类型,必须得要引用类型)
ArrayList<String> list = new ArrayList<String>();
list.add("my");
list.add("abc");
//list.add(10); 加了泛型以后,在编译的时候就报错了
集合如果没有指定泛型,取出来的元素都是Object
for (Object obj : list) {
String str = (String) obj;
System.out.println(str.length());
}
for (String str : list) {定义了泛型,就不需要再去强转
System.out.println(str.length());
}
泛型使用注意点及本质
泛型注意点:
1.泛型前后类型必须得要一致
2.从java 1.7开始,后面的类型可以不写 new ArrayList<>();菱形语法
3.泛型是没有继承的
//错误的写法 ArrayList<Object> list = new ArrayList<String>();
4.泛型其实是一个语法糖 (本质还是Object 内部其实还是要进行强转的)
Point<String> p1 = new Point<>();
p1.x = "20";
p1.y = "10";
System.out.println(p1.getY());//里面会进行强转
自定义泛型方法
class Test<T>{
T name;
public T getName() {
return name;
}
}
自定义泛型方法
方法当中定义的泛型,是在使用方法时,参数传递确定具体是什么类型
方法想要单独使用泛型,必须得要有参数才有意义
class Student {
<T> void test(T a) {
System.out.println(a.getClass());
}
自定义方法也可以自定义泛型
static <E> void test2(E name) {
System.out.println(name.getClass());
}
添加泛型返回值
static <E> E test3(E name) {
System.out.println(name.getClass());
return name;//需要用传入什么类型就用什么类型来接收
}
}
1.泛型类:在类上面定义的泛型,在创建对象的时候,要指明泛型的类型
Test<String> t = new Test<String>();
Test<Integer> t1 = new Test<Integer>();
泛型当中定义的泛型只能用在普通方法上面
不能使用在静态方法上面
因为静态方法是直接使用类名调用,泛型是在创建对象的时候,才去指定类型
2.泛型方法:就是在方法上面添加了泛型
单独对一个方法上面声明泛型
方法当中定义的泛型
new Student().test("adf");
Student.test2(12);
Integer i = Student.test3(12);
System.out.println(i);//class java.lang.Integer
泛型通配符
通配符:不知道使用什么类型来接收的时候,可以用?来表示未知
List<Integer> list = new ArrayList<>();
test(list);
只用来接收使用
不能做添加操作
List<?> list2 = new ArrayList<>();
list2.add("a");
泛型的上限和下限
泛型的的上限
:用来限定元素的类型必须继承指定类型(Number的子类)<指定类型或者指定类型的子类>
static void test(List<? extends Number> list) {
}
需要用正确的类型接收指定继承的类型
List<Integer> list = new ArrayList<>();
test(list);
List<String> list2 = new ArrayList<>();
test(list2);//不是继承Number,报错
泛型的的下限
:用来限定元素的类型必须继承指定类型(Number的父类)<指定类型或者指定类型的父类>
static void test2(List<? super Number> list) {
}
List<Number> list3 = new ArrayList<>();
//List<Object> list3 = new ArrayList<>(); 可以
test(list3);//可以接收Number 的 父类和它自己类型本身
泛型擦除
泛型擦除(把泛型去除)
List<String> list = new ArrayList<>();
list.add("a");//定义了泛型,类型限定了传参
List list2 = null;
list2 = list ;//把list当中的泛型擦除掉,没有了泛型
list2.add(10);
list2.add("aaa");//什么类型都能放进去,编译过后也是这样,变成了Object类型
List与数组的互相转换
数组转成集合
int[] arr = { 10, 20, 30 };
List list = Arrays.asList(arr);//---->List<int[]> list = Arrays.asList(arr);泛型类型:数组
运行时报错,数组转换为集合并不能添加元素
list.add(10);//报错
那为什么要转为集合呢?因为转过之后,可以面向对象的方式来操作数组(除了不能添加和删除元素之外,集合当中的其他东西都是可以使用的)
@SafeVarargs
@SuppressWarnings("varargs")
public static <T>方法泛型 List<T>返回一个传入什么类型的List asList(T...可变参数,本质是一个数组 a) {
return new ArrayList<>(a);
}
基本数据类型,转成集合时,是把基本数据类型的数组,当作是一个对象
在开发当中不会把基本数据类型数组转成集合,没意义
System.out.println(list.size());//1 int[] arr = { 10, 20, 30 };
引用数据类型的数组才去转成集合
Integer[] arr2 = { 10, 20, 30 };
List list2 = Arrays.asList(arr2);//---->List<Integer> list2 = Arrays.asList(arr2);泛型类型:对象
System.out.println(list2);//[10, 20, 30]
System.out.println(list2.size());//3
集合转数组
List<String> list3 = new ArrayList<>();
list3.add("a");
list3.add("b");
list3.add("c");
Object[] o = list3.toArray();//以前Object数组转法
String[] strArr = list3.toArray(new String[0]);
//静态开辟空间,如果开辟的空间少于size,会自动的创建一个和size一样空间大小
System.out.println(Arrays.toString(strArr));//[a, b, c]
String[] strArr2 = list3.toArray(new String[10]);
//<T> T[] toArray(T[] a);
System.out.println(Arrays.toString(strArr2));
//[a, b, c, null, null, null, null, null, null, null]
集合嵌套集合
需求
:学科担纲总有很多班级,班级当中又有很多学生
public static void main(String[] args) {
Person per1 = new Person("zs");
Person per2 = new Person("ls");
//班级1
List<Person> c1 = new ArrayList<>();
c1.add(per1);
c1.add(per2);
Person per3 = new Person("zs1");
Person per4 = new Person("ls2");
//班级2
List<Person> c2 = new ArrayList<>();
c2.add(per3);
c2.add(per4);
//学科(集合当中又存储集合)
List<List<Person>> x = new ArrayList<>();
x.add(c1);
x.add(c2);
//把所有班级当中的学生姓名打印出来
for (List<Person> g : x) {
//取出每一个班级
for (Person person : g) {
System.out.println(person.name);
}
}
}
Set集合特性演示
set当中存的元素是无序的,里面没有重复的元素
set已经覆盖了toString方法
HashSet<String> hs = new HashSet<>();
boolean res1 = hs.add("a");
boolean res2 = hs.add("a");//添加不成功
hs.add("b");
hs.add("c");
hs.add("1");
System.out.println(res1);//true
System.out.println(res2);//false
System.out.println(hs);//[a, 1, b, c]无序存放排列
Iterator it = hs.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}//a 1 b c
能够使用迭代器都可以使用增强for循环(foreach)
for (String str : hs) {
System.out.println(str);
}
Set自定义对象相等判断
想要在Set中自定义对象中去重
1.覆盖equals方法
2.覆盖hasCode方法
如果两个自定义对象,属性完全相同,那么就判定你是同一个对象
public class Demo1 {
public static void main(String[] args) {
Set<Person> s =new HashSet<>();
s.add(new Person("zs",19));
s.add(new Person("zs",19));
s.add(new Person("ls",30));
s.add(new Person("ls",30));
s.add(new Person("ls",30));
System.out.println(s.toString());//[Person [name=zs, age=19], Person [name=ls, age=30]]
}
}
class Person{
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override//覆盖toString方法
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
@Override//覆盖hashCode
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override//覆盖equals
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
hashCode
每一个对象都会有一个hashCode
hashCode就是跟内存地址对应的一个编号
编号就是对应你内存的地址
每一个对象的hashCode都是不一样的
Cat c = new Cat("mm");
System.out.println(c);
System.out.println(c.hashCode());
不覆盖hashCode会帮你生成一个唯一的值
@Override
public int hashCode() {
return 10;
}
HashSet去重原理分析
class Cat {
String name;
Cat(String name) {
this.name = name;
}
// 不覆盖hashCode会帮你生成一个唯一的值(每次生成的值都不一样)
/*@Override
* public int hashCode() {
System.out.println("执行了hashCode");
return 10;
}
public boolean equals(Object obj) {
System.out.println("执行了equals");
return false;
}*/
选择系统覆盖hashCode和equals方法
@Override
public int hashCode() {
//如果属性相同,返回相同的hashCode
//属性不同,hashCode就不相同
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Cat other = (Cat) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Cat c1 = new Cat("mm");
Cat c2 = new Cat("mm");
Set<Cat> s = new HashSet<>();
添加一个对象时,会调用对象的hashCode
hashCode不相同的时候,c2对象和集合当中的hashCode不相同,就不会调用equals
当hashCode值相同的时候才会去调用equals方法 如果equals返回为true,就不添加到集合当中
s.add(c1);
s.add(c2);
System.out.println(s);//equals为false的时候,会添加元素进去[集合.泛型.Cat@a, 集合.泛型.Cat@a]
LinkedHashSet
LinkedHashSet是HashSet的子类
它底层是用链表实现的,它是set集合当中,唯一一个保证元素是怎么存怎么取出来的
HashSet 能够保证元素的唯一性
HashSet<String> set1 = new HashSet<>();
set1.add("a");
set1.add("b");
set1.add("1");
set1.add("e");
System.out.println(set1);//使用普通的HashSet,顺序会掉乱[a, 1, b, e]
LinkedHashSet<String> set2 = new LinkedHashSet<>();
set2.add("a");
set2.add("1");
set2.add("c");
set2.add("e");
System.out.println(set2);//[a, 1, c, e]
生成1到20之间不重复的随机数
获取十个随机数
不允许有重复
//1.使用Random来生成随机数 创建Random
Random r = new Random();
//2.创建存放生成结果的集合,HashSet
HashSet<Integer> hs = new HashSet<>();
//3.当HashSet.size大于10的时候,就不放,否则一直生成往里面放
while(hs.size()<10) {
//4.生成1到20之间的随机数
int res = r.nextInt(20)+1;//nextInt(20)生成的是0-19的随机整数
//5.添加到集合当中
hs.add(res);
}
System.out.println(hs);//[1, 17, 18, 3, 5, 6, 7, 11, 12, 15]
去除重复字符串练习
//aaabbbccc
//abc
//1.获取从键盘上输入的字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入一个字符串:");
String line = sc.nextLine();
//2.拿字符串当中的每一个字符
char[] arr = line.toCharArray();
System.out.println(Arrays.toString(arr));
//3.取出每一个元素,添加到一个可以去重的集合当中
LinkedHashSet<Character> hs = new LinkedHashSet<>();
for(int i = 0 ; i<arr.length;i++) {
char c = arr[i];//取出数组当中的所有元素
hs.add(c);
}
TreeSet
Tree它是无序的(不是按照你添加的顺序展示的)
对添加的元素进行排序
TreeSet是用来对元素进行排序,里面也是可以保证元素唯一
//按数字排序
TreeSet<Integer> set = new TreeSet<Integer>();
set.add(10);
set.add(2);
boolean res = set.add(2);
System.out.println(res);//false
set.add(9);
set.add(6);
System.out.println(set);//[2, 6, 9, 10]
//按字母排序
TreeSet<String> set2 = new TreeSet<String>();
set2.add("a");
set2.add("b");
set2.add("d");
set2.add("c");
set2.add("g");
System.out.println(set2);//[a, b, c, d, g]
//汉字是按照unicode-->里面已经包含了Assic码
TreeSet<String> set3 = new TreeSet<String>();
set3.add("我");
set3.add("的");
set3.add("名");
set3.add("字");
System.out.println(set3);//[名, 字, 我, 的]
System.out.println('我' + 0);//25105
System.out.println('的' + 0);//30340
System.out.println('名' + 0);//21517
System.out.println('字' + 0);//23383
//按字母排序,比较了前面,前面相同再比后面
TreeSet<String> set4 = new TreeSet<String>();
set4.add("aa");
set4.add("ac");
set4.add("aba");
System.out.println(set4);//[aa, aba, ac]
TreeSet添加自定义对象
class Person implements Comparable<Person>{
String name;
int age;
Person(String name,int age){
this.name = name;
this.age = age;
}
//如果返回0的话,只添加一个元素
//如果是一个正数,都能添加到集合当中,顺序怎么添加的,怎么显示
//如果是一个负数,顺序怎么添加的,倒序显示(第一个添加的,显示到最后)
@Override
public int compareTo(Person o) {
return -1;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Demo1 {
public static void main(String[] args) {
//TreeSet当中存放的类型必须得是同一类型
//TreeSet set = new TreeSet<>();
//set.add(10);
//set.add("a");
//自定义对象不能直接添加到TreeSet当中
//想要添加到TreeSet当中必须得要遵守一定的规则
//实现接口(Comparable)
//还得覆盖当中的compareTo方法
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("zs",20));
set.add(new Person("ls",21));
set.add(new Person("ww",22));
set.add(new Person("zl",23));
System.out.println(set);
//retrun为0的时候,[Person [name=zs, age=20]]只能显示头个元素
//把return的结果改为任何一个正数,能全部显示在集合当中
}
}
TreeSet二叉树原理分析
二叉树:在数据结构中,就像一棵树发叉,根节点线下衍生子节点,一个节点发两个叉
TreeSet自定义对象属性排序
想要进行自定义对象排序,对象必须实现接口Comparable,覆盖comparaTo方法
class Person implements Comparable<Person> {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person obj) {
//进行年龄排序
int num = this.age - obj.age;//this.age当前的age,减去下一个传入来的age的值
/*if (num == 0) {//如果num的值为0
return this.name.compareTo(obj.name);//再进行名字的比较
} else {
return num;//非0直接返回num值
}*/
return num == 0 ? this.name.compareTo(obj.name): num;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
需要进行年龄排序
TreeSet<Person> set = new TreeSet<>();
set.add(new Person("张三", 20));
set.add(new Person("李四", 25));
set.add(new Person("王五", 22));
set.add(new Person("赵六", 21));
set.add(new Person("赵六1", 21));
System.out.println(set);
//[Person [name=张三, age=20], Person [name=赵六, age=21], Person [name=赵六1, age=21], Person [name=王五, age=22], Person [name=李四, age=25]]
TreeSet比较器
默认情况下,比较是会调用对象的compareTo进行比较
如果传入了比较器,就不会调用compareTo,就会使用你传入的比较器进行比较
默认是使用字母的顺序进行比较的(compareTo)
TreeSet<String> set = new TreeSet<String>();
set.add("aaaaa");
set.add("z");
set.add("wc");
set.add("wc");
set.add("wzb");
set.add("nba");
System.out.println(set);
//使用比较器进行比较,比较字符串的长度排序
//1.实现一个接口comparator
//2.定义一个类来去实现这个接口
//3.覆盖它里面的方法
TreeSet<String> set2 = new TreeSet<String>(new ComparaLength());//需要传入比较器对象
set2.add("aaaaa");
set2.add("z");
set2.add("wc");
set2.add("wc");
set2.add("wzb");
set2.add("nba");
System.out.println(set2);//[z, wc, nba, wzb, aaaaa]
class ComparaLength implements Comparator<String>{
@Override
//1.代表当前正在添加的对象
//2.代表集合当中的对象
public int compare(String o1, String o2) {
System.out.println("调用了比较器当中的compare");
int lenght = o1.length() - o2.length();//主要是比长度
return lenght == 0 ?o1.compareTo(o2):lenght ;//如果长度相等,再去比内容
}
}
Map映射关系概述
Map:映射关系
A集合 B集合 (ArrayList LinkedList Vector HashSet LinkedHashSet TreeSet)
A集合当中的元素不能重复(Set),A集合当中的每一个元素称它是一个Key
A集合当中的每一个元素,都可以在B集合当中找到一个唯一的一个值与之对应
B集合当中的元素可以重复(list),B集合当中的每一个元素称它是一个Value
Map当作是一个字典(dict)
Key-Value(entry)
Map添加元素
往AB两个集合当中添加元素,并且关联在一起(value会跟着key走)
map当中的元素并不是有序的
key1=value1 entry键值对
Map<String,Object> map = new HashMap<String,Object>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
map.put("key4", "value4");
map.put("key5", "value5");
System.out.println(map);//{key1=value1, key2=value2, key5=value5, key3=value3, key4=value4}
//获取A集合
Set<String> set = map.keySet();
System.out.println(set);//[key1, key2, key5, key3, key4]
//获取B集合
Collection<Object> values= map.values();
System.out.println(values);//[value1, value2, value5, value3, value4]
Map修改元素
Map<String,Object> map = new HashMap<>();
//添加功能
Object res1 = map.put("张三", 20);
Object res2 = map.put("李四", 21);
Object res3 = map.put("王五", 22);
System.out.println(map);//{李四=21, 张三=20, 王五=22}
//如果Key值是第一次添加,返回值就是null
System.out.println(res1);
System.out.println(res2);
System.out.println(res3);//null
//添加的Key并不是第一次存在,就用value把以前的值给替换掉,返回以前的值
Object res4 = map.put("张三", 30);
System.out.println(map);//{李四=21, 张三=30, 王五=22}
System.out.println(res4);//20
Map常用方法
1.添加功能
2.删除功能
//2.1可以根据key来删除元素(key-value)
map.remove("张三");//{李四=21, 王五=22}
System.out.println(map);
//2.2清空map当中所有的key-value
map.clear();
System.out.println(map);//{} 里面一个元素都没有了
//3.长度功能,查看有几个键值对
//map当中有几个key-value
System.out.println(map.size());//0
Map获取元素及遍历元素
// 双列集合
Map<String, Object> map = new HashMap<>();
Object res1 = map.put("张三", 20);
Object res2 = map.put("李四", 21);
Object res3 = map.put("王五", 22);
获取元素
1.获取一个元素
根据相关的key可以获取相关联的value
Object values = map.get("张三");
System.out.println(values);// 20
2.获取所有元素(遍历)
map当中是没有迭代器,集合没有迭代器,你就不能用快速遍历(foreach)
// 取出所有的key
Set<String> allKeys = map.keySet();//map.keySet() 获取全部key返回Set装
Iterator<String> it = allKeys.iterator();
while (it.hasNext()) {
// 取出key值
String key = it.next();
Object val = map.get(key);
System.out.println( key + "="+val);
//李四=21,张三=20,王五=22
}
//set能使用迭代器,就能使用foreach
for (String key : allKeys) {
System.out.println(key+"="+map.get(key));
}
Map通过entry对象遍历元素
// 双列集合
Map<String, Object> map = new HashMap<>();
Object res1 = map.put("张三", 20);
Object res2 = map.put("李四", 21);
Object res3 = map.put("王五", 22);
//获取所有的key-value对象entry
Set<Map.Entry<K, V>> set = map.entrySet();