1.泛型和链表
1.泛型
1.什么是泛型
1.英文含义:
generic paradigm一般类型
2.泛型的含义:
泛型是一种未知的数据类型不像 int string 一样直接去内存中申请空间而是在确定泛型的类型的时候才会去申请内存空间。
3.应用场景:
当我们不知道用什么类型的时候可以使用泛型。
4. 泛型的作用:
泛型可以看作是一个变量 用来接收数据(像参数一样),接收的是一种类型
案例:
T t—接收—>10
int t=10;
提示:
<>号中的标识表示符是任意可以是任意字符、< T>
E的英文全拼是 element 元素
T的英文全拼是 Type 类型(自定义泛型)
案例:
public static void func(T[]arr)
{
}
注:
public static void func(T[]arr){}是一种模板可以用来存储一组数据然后返回一个自己想要的数据。
案例:
给你一组学生 返回学生中年龄最大的
给你一堆苹果 返回苹果中最大的
5.经典脚本
public class ArrayList
{
public boolean add(E e)
public E get(int index )
}
分析1:
为什么动态数组可以作为泛型的典型案例呢?
如果ArrayList不用泛型就无法存储自己想要存储的数据
分析2:
泛型可以当作参数和返回值
当泛型出现在,o就表示一种类型了
分析3:
在什么情况下确定泛型的类型?
1.泛型接口/类
创建子对象或者对象的时候确定
2.方法
通过参数可以推断
2.泛型包含泛型的方法、泛型类、泛型接口
1.泛型方法:
2.泛型类:
3.泛型接口:
3.泛型的好处:
1.避免类型转换带来的麻烦
案例:
public static void main05(String[] args) {
ArrayList list=new ArrayList<>();
list.add(1)
int number=list.get(0);
}
案例:
public static void main05(String[] args) {
ArrayList list=new ArrayList<>();
//类型转换麻烦
list.add(1);
list.add(“1”);
list.add(1);
list.add(new Student(1,“1”));
int number=(int)list.get(0);
}
注:存储什么类型 取出就是什么类型
2.把运行期异常代码 提前到了编译期
案例:
案例:
public static void main05(String[] args) {
ArrayList list=new ArrayList<>();
list.add(1);
list.add(“1”);
list.add(new Student(1,“1”))
int number=list.get(0);
}
注:
强烈建议不使用 ArrayList list=new ArrayList ();
如果向list添加一些没有联系的一些数据时在查找数据的时候会变得非常麻烦尤其是在做增删改查的时候
4.泛型的弊端:
只能存储固定类型的数据
5.自定义泛型方法和接口
冒泡排序
问题1:参加排序类型是固定的
问题2:排序的条件是固定的
package CollectionDemo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import ExtendsDemo7.AirPlane;
import ExtendsDemo7.Dart;
import ExtendsDemo7.Hand;
import ExtendsDemo7.IThrowing;
/**
* 集合
*
*
* @author lenovo
*
*/
public class CollectionTest {
public static void main01(String[]args) {
Collection<Integer> coll=new ArrayList<Integer>();
Collection<Integer> coll2=new LinkedList<>();
//接口指向实现类
//接口不单单只指向一个实现类
coll.add(1);//(多态)
//重写完之后的add接口中的add是没有方法体的
/**
* 接口中的都是抽象的只是定义了一个规范
*
*
*
*/
coll.add(1);
coll.add(2);
if(coll.contains(1)) {//判断是否包含某个元素的
System.out.println("包含1");
}
if(coll.isEmpty()) {
System.out.println("空了");
}
//coll.remove(1);//移除一个元素
//coll.clear();
System.out.println(coll);
coll.toArray();//把集合元素存储到数组
//增强for循环
//增强for循环的提示:
//foreach-iterate over an array Iterable
//循环for(要遍历的数据类型 变量名:要遍历的集合)
//增强for循环与for循环相比 的区别:
//不需要知道集合的长度而且看起来更加直观
System.out.println("+++++++++++++++++");
for(Integer integer:coll) {
System.out.println(integer);
}
}
public static void main02(String[]args) {
int []arr= {1,2,3};
for(int i:arr) {
System.out.println(i);
//问题:Hand 扔东西
}
Hand hand=new Hand();
//创建一个手类
hand.list=new ArrayList<>();
//手类里边有一个集合然后初始化
hand.list.add(new Dart(1000,100));
hand.list.add(new AirPlane());
//集合里边存储IThrowing类型的对象
//集合里边存实现接口的对象
hand.throwFunc();
//想要实现迭代必须满足迭代的条件
//第一步满足迭代的条件 实现接口Iterable
for(IThrowing i:hand) {
System.out.println(i);
//hand需要实现一个接口
//只能通过 java.lang.Iterable 的数组或实例进行迭代
//hand没有实现iterate接口
}
//增强for循环的内部脚本
//1.获取迭代器对象 对象指向数组
Iterator<IThrowing>iterator=hand.iterator();
//2.迭代器移动
iterator.hasNext();//判断是否有下一个元素
// if(iterator.hasNext()) {
// IThrowing i=iterator.next();//获取迭代器上的数据
// }
// if(iterator.hasNext()) {
// IThrowing i=iterator.next();//获取迭代器上的数据
// }
// if(iterator.hasNext()) {
// IThrowing i=iterator.next();//获取迭代器上的数据
// }
//重复做一件事而且不知到循环次数
while(iterator.hasNext()) {
IThrowing i=iterator.next();
System.out.println(i);
}
}
//泛型
public static void main03(String[]args) {
int[]arr= {213};
float[]arr1= {2,1,3};
Arrays.parallelSort(arr);
for(int i:arr) {
System.out.println(i);
}
}
public static<T>void sort3(T[]arr,Comparator<T>com){
for(int i=0;i<arr.length-1;i++) {
for(int j=0;j<arr.length-1;j++) {
//number>0
if(com.compare(arr[i], arr[i+1])>0){
T temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
public static void main(String[]args) {
Integer [] arr1 = {2,1,3};//int是基本数据类型不是clss T 代表的是一种类型(int不是引用类型)
Float []arr2= {1.0f,2.0f,3.0f};//泛型方法的T确定是通过参数推断出来的
Student s1=new Student(10,"1");
Student s2=new Student(18,"1");
Student s3=new Student(112,"1");
Student[]stuArr= {s1,s2,s3};
//sort(stuArr,new ComStudent());
sort3(stuArr,new Comparator<Student>() {
@Override
public int compare(Student t1, Student t2) {
// TODO 自动生成的方法存根
if(t1.age>t2.age) {
return 1;
}
else if(t1.age<t2.age) {
return -1;
}
else {
return 0;
}
}
});
for(Student student :stuArr) {
System.out.println(student.age);
}
}
public static<T>void sort(T[]arr,Comparator<T>com){
for(int i=0;i<arr.length-1;i++) {
for(int j=0;j<arr.length-1;j++) {
if(com.compare(arr[i], arr[i+1])>0){
T temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
}
package CollectionDemo;
/**
*
*
* 泛型接口
* 接口中会提供一个排序标准 对什么对象进行排序不知道
* @author lenovo
*
* @param <T>
*/
public interface CompareHandler<T> {
boolean compare(T t1,T t2);
}
class ComClass implements CompareHandler<Integer> {
@Override
public boolean compare(Integer t1, Integer t2) {
if(t1>t2) {
return true;
}
else {
return false;
}
}
}
//class ComClass1 implements CompareHandler<Float>{
//
// public boolean compare(Float t1, Float t2) {
// if(t1>t2) {
// return true;
// }
// else {
// return false;
// }
// }
//
//
//
//}
class ComStudent implements CompareHandler<Student>{
public boolean compare(Student t1,Student t2) {
if(t1.age>t2.age) {
return true;
}
else {
return false;
}
}
}
package CollectionDemo;
public class Student {
int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, String name) {
super();
this.age = age;
this.name = name;
}
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SrsFSbGw-1600951594116)(C:\Users\lenovo\Desktop\泛型\2020-09-24_161533.png)]
3.通用的排序方法
1.任意类型的数组都可以参加排序
2.指定任意的排序条件
案例:
public static void sort(T [] arr,CompareHandler com){
//Arrays.sort
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1; j++) {
// 父类的引用调用子类的方法
if (com.compare(arr[i], arr[i+1])) {
T temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
案例:
public static void main06(String[] args) {
Integer [] arr={11,2,31};//int 基本数据类型 不是引用类型
Float [] arr1={1.0f,2.0f,3.0f};
Student s1=new Student(10, "1");
Student s2=new Student(8, "1");
Student s3=new Student(12, "1");
Student [] stuArr={s1,s2,s3};
//sort(stuArr, new ComStudent());
sort3(stuArr, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
if (o1.age>o2.age) {
return 1;
}
else if (o1.age<o2.age) {
return -1;
}
else {
return 0;
}
}
});
for (Student student : stuArr) {
System.out.println(student.age);
}
}
public static <T> void sort3(T [] arr,Comparator<T> com){
//Arrays.sort
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1; j++) {
// 父类的引用调用子类的方法
//number>0
if (com.compare(arr[i], arr[i+1])>0) {
T temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
}
6.泛型的规则:
- 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前。
- 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
- 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
- 泛型方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char的等)。
7.泛型的通配符
当使用泛型类型或接口时,传递的数据,泛型类型不确定 可以使用通配符<?>表示
缺点:
一旦使用通配符之后 只能调用Object共性的方法了
案例:
```
package CollectionDemo;
import java.util.ArrayList;
import java.util.Collection;
public class GenericClassTest {
public static void main01(String[]args) {
GenericClass gene=new GenericClass<>();
gene.setMvp(“123”);
System.out.println(gene.toString());
}
public static void main(String[]args) {
Collectioncoll=new ArrayList<>();
Collection coll1=new ArrayList<>();
getElement(coll);
}
public static void getElement(Collection<?>coll) {
//Collection<?>要的是固定集合的参数
}
}
```
注:
Collection和Collection<?>的的区别:
Collection的含义是不知道要操作的类型是什么要通过对象或者方法来确定,而Collection<?>是在 Collection<?>的前提之上知道了对象的类型想要获取对象的值并无需考虑对象的具体类型的时候即只要获取值的时候使用collection<?>
8.泛型约束
1.泛型的上线
格式:
类型<? extends 类>对象名称
含义:
?必须要继承某一类型
意义:
只能接收该类型及其子类
2.泛型的下线
格式:
类型<?super类>对象名称
意义:
只能接收该类型及其父类型
案例:
```
public static void main(String[]args) {
Collectioncoll=new ArrayList<>();
Collection coll1=new ArrayList<>();
Collectioncoll2=new ArrayList<>();
Collectioncoll3=new ArrayList<>();
getElement2(coll);//(访问了父类)//只能放Number及Number的子类
getElement2(coll2);//(访问了子类)
getElement2(coll3);//(访问了本身)
getElement3(coll);
getElement3(coll1);
getElement3(coll2);
getElement3(coll3);
getElement3(coll3);
//已知Object 所有类的父类
//String是一个普通类的引用类
//Integer类 继承自Number
}
//通配符美哟任何限制
public static void getElement(Collection<?>coll) {
//Collection<?>要的是固定集合的参数
}
public static void getElement2(Collection<?extends Number>coll) {
//Collection<?>要的是固定集合的参数
}
public static void getElement3(Collection<?super Number>coll) {
//Collection<?>要的是固定集合的参数
}
```
9.数据结构
数据结构的意义:
数据结构是判断一门编程好坏的标准之一
常用的数据结构:
线性数据结构:
数组:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i5e2KtCe-1600951594117)(C:\Users\lenovo\Desktop\泛型\2020-09-24_173443.png)]
链表:
树:
数组+树 表
图:
数组:(一块连续的区域)
int []arr
优点:
获取元素块
动态数组:(牵一发而动全身)(一块连续的区域)
ArrayList
动态数组的问题:
没有解决数组插入/删除麻烦的问题
链表:(牵一发而痛自己)
链表:只要有能容易一个元素的内存就可以
优点:
链表的插入和删除的效率高
缺点:
获取元素效率低(每次都是从头开始找)
查找链表:
1.head头
2.end 尾
3.next(下一个)
next--------->next=(第一个元素)
双向链表:
链表的内部样式:
案例:
package LinkedListDemo;
//链表
//ArrayList<E>
public class LinkedList<T> {
//长度
private int m_unsize=0;//只读
public int getM_unsize() {
return m_unsize;//只读的
}
//链表的头
//链表的尾巴
public Node<T> m_head;//存储链表的头
public Node<T> m_end;//存储链表的尾
//判空
public boolean isEmpty() {
return m_head==null?true:false;
//三元表达式判断链表为空
}
//添加
public void push_back(T data) {
//给数据元素创建一个结点
Node<T> pnewNode=new Node(data);
if(isEmpty()) {
m_head=pnewNode;//如果还没有元素让新元素作为头结点
}else {
m_end.setM_pNext(pnewNode);
//不为空 设置尾、结点的下一个尾新结点
m_unsize++;//长度+1
}
m_end=pnewNode;//更新新结点
}
//插入前插和后插
public boolean InserForward(T pdata,int pos) {
if(isEmpty()) {
System.out.println("空链表");
return false;
}
//插入位置超出总链表长度
else if(pos>=m_unsize) {
System.out.println("去你的吧");
return false;
}
if(pos==0) {//代表有一个头元素
//如果插入位置为开始
Node<T> pnewNode=new Node<>(pdata);
pnewNode.setM_pNext(m_head);
//设置新结点的下一个为原来的头结点
m_head=pnewNode;
m_unsize++;//更新头结点
}
else {
Node<T>pFront=m_head;
//记录的插入位置的上一个开始的时候是头结点
Node<T>ptemp=m_head;
//遍历的
int tempPos=1;
//更新位置 tempPos<pos
while(ptemp.getM_pNext()!=null&&tempPos<=pos ){
//获取插入位置上的结点和他的前结点
//因为要在插入位置的结点和他的前结点之间插入元素
pFront=ptemp;
ptemp=ptemp.getM_pNext();
tempPos++;
}
//插入元素
Node<T>pnewNode=new Node<>(pdata);//插入数据的结点
pnewNode.setM_pNext(ptemp);
pFront.setM_pNext(pnewNode);
m_unsize++;
}
return true;
}
}
package LinkedListDemo;
//每一个结点可以看成一个类
//结点类 结点中存放着数据date和指向下一个结点的引用
public class Node<T> {
private T data;//需要保存的数据元素
private Node<T> m_pNext;//后继结点的引用 下一个next结点
private Node<T> m_pFront;//前驱节点的引用
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public Node<T> getM_pNext() {
return m_pNext;
}
public void setM_pNext(Node<T> m_pNext) {
this.m_pNext = m_pNext;
}
public Node<T> getM_pFront() {
return m_pFront;
}
public void setM_pFront(Node<T> m_pFront) {
this.m_pFront = m_pFront;
}
public Node() {
this.data=null;
m_pFront=m_pNext=null;//两个引用设置为空
}
public Node(T data) {
this.data = data;//初始化数据
m_pFront=m_pNext=null;//两个引用设置为空
}
}
package LinkedListDemo;
public class Test {
public static void main(String[]args) {
LinkedList<Integer>list=new LinkedList<>();
list.push_back(1);
list.push_back(2);
list.push_back(3);
list.InserForward(100, 1);
int number=list.m_head.getM_pNext().getData();
int number2=list.m_end.getData();
System.out.println(number);
System.out.println(number2);
}
}
data = data;
}
public Node getM_pNext() {
return m_pNext;
}
public void setM_pNext(Node m_pNext) {
this.m_pNext = m_pNext;
}
public Node getM_pFront() {
return m_pFront;
}
public void setM_pFront(Node m_pFront) {
this.m_pFront = m_pFront;
}
public Node() {
this.data=null;
m_pFront=m_pNext=null;//两个引用设置为空
}
public Node(T data) {
this.data = data;//初始化数据
m_pFront=m_pNext=null;//两个引用设置为空
}
}
package LinkedListDemo;
public class Test {
public static void main(String[]args) {
LinkedListlist=new LinkedList<>();
list.push_back(1);
list.push_back(2);
list.push_back(3);
list.InserForward(100, 1);
int number=list.m_head.getM_pNext().getData();
int number2=list.m_end.getData();
System.out.println(number);
System.out.println(number2);
}
}