这些类型的数据。
泛型通配符是什么?
问号(英文问号)
泛型通配符使用有什么注意事项?
泛型不支持多态
集合使用泛型通配符,方法参数含有泛型,就用不了。
受限泛型
受限泛型是指,对泛型做了约束,给泛型指定类型时,只能是某个类型及其父类型或者某个类型及其子类型。
泛型的下限<? super 类型> //只能是某一类型,及其父类型,其他类型不支持
泛型的上限<? extends 类型> //只能是某一个类型,及其子类型,其他类型不支持
2 代码实践
基于上一个知识点,定义方法
show1方法,参数只接收元素类型是Number或者其父类型的集合
show2方法,参数只接收元素类型是Number或者其子类型的集合
泛型的上限格式和含义是什么?
<? extends 类型> 只能是本类型或者子类型
泛型的下限格式和含义是什么?
<? super 类型> 只能是本类型或者父类型,到顶了只能是Object
数据结构
1 数据结构介绍
数据结构是计算机存储、组织数据的方式。通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率。数据结构往往同高效的检索算法和索引技术有关。
2 常见的数据结构
栈
队列
数组
链表
树(二叉树,二叉平衡树,红黑树)
哈希表(数组+链表+红黑树)
数据结构-栈
1 栈数据结构特点
栈(stack):是一种受限的线性表结构,不能在中间任何位置进行数据的操作,只能在指定的一端进行存或者取。
特点:先进后出,后进先出。
生活中中有哪些场景可以体现栈结构:
手枪的弹夹。最先压入的子弹,最后打出。
乒乓球桶。最先放进去的求,最后才能拿出。
名词解释:
压栈(入栈):将数据存入栈结构
弹栈(出栈):将数据弹出栈结构
数据结构-队列
2 队列数据结构的特点
队列(Queue )结构,也是一种线性表结构。要求数据只能一端存储,一端取出。
特点:先进先出。
生活中的场景:排队安检,食堂吃饭排队。
数据结构-数组
1 数组结构介绍
数组在内存中是由一片连续的空间组成。每个元素都是相邻的有索引,可以通过索引快速定位元素的位置。
数组结构是稳定的,一旦定义,结构就不能改变。如果要增删元素,需要借助新的数组,将老数组的元素拷贝到新数组中。
数组的特点:查询快,增删慢
2 数组元素的增删
新增元素:
需要创建一个新数组,将指定新元素存储在指定索引位置,再把原数组元素根据索引,复制到新数组对应索引的位置
删除元素:
需要创建一个新数组,把原数组元素根据索引,复制到新数组对应索引的位置,原数组中指定索引位置元素不复制到新数组中。
数组结构-链表
1 链表结构介绍
链表(linked list ),由一系列结点node(链表中每一个元素称为结点)组成,节点就是一个个的对象。节点对象在内存中无需连续空间,通过地址进行相连。
每个结点包括两个部分:
数据域:用来保存数据
指针域:存储地址,用来连接其他节点
2 单向链表
每个节点,只有下一个节点的地址
3 双向链表
每个节点,即有下一个节点的地址,又有上一个节点的地址
4 链表特点
链表在获取数据时不能像数组一样使用索引来定位获取,只能从开始节点位置或结束节点位置(双向链表)进行逐个查询,因此查询数据相较数组来讲是比较慢的。
链表在增删数据时无需新建新链表,摧毁旧链表。只要在局部位置进行地址的改变就可以,效率相比数组是交高效
简而言之,链表结构特点: 查询慢, 增删快
栈结构的特点?
先进后出
队列结构的特点?
先进先出
数组结构的特点
增删慢,查询快
链表结构的特点?
增删快,查询慢
List
List集合是Collection集合子类型,继承了所有Collection中功能,比Collection约束更加详细,严谨。
特点如下:
元素具备索引 【有索引】
元素可以重复存储(两个对象如果equals方法调用后返回true)【可重复】
元素的存取是有序的【有序】
ArrayList:底层结构就是数组【查询快,增删慢】
Vector:底层结构也是数组(线程安全,同步安全的,低效,用的就少)
LinkedList:底层是链表结构(双向链表)【查询慢,增删快】
3 List中常用的方法
List继承了Collection中所有方法,元素具备索引特性,因此新增了一些含有索引的特有方法,如下:
- public void add(int index, E element): 将指定的元素,添加到该集合中的指定位置上。
- public E get(int index):返回集合中指定位置的元素。
- public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。
- public E set(int index, E element):用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
List集合有什么特点?
1. 有序 2. 可重复的 3. 有索引
List集合有哪些常用的子类及底层数据结构是啥?
ArrayList:数组结构
LinkedList:双向链表
Vector:数组结构
List集合有哪些常用的特有方法?
add remove set get
LinkedList集合
LinkedList底层结构是双向链表。每个节点有三个部分的数据,一个是保存元素数据,一个是保存前一个节点的地址,一个是保存后一个节点的地址。可以双向查询,效率会比单向链表高。
2 LinkedList特有方法
public void addFirst(E e):将指定元素插入此列表的开头。public void addLast(E e):将指定元素添加到此列表的结尾。public E getFirst():返回此列表的第一个元素。public E getLast():返回此列表的最后一个元素。public E removeFirst():移除并返回此列表的第一个元素。public E removeLast():移除并返回此列表的最后一个元素。public E pop():从此列表所表示的堆栈处弹出一个元素。(底层实现就是removeFist)public void push(E e):将元素推入此列表所表示的堆栈。(底层就是addFirst)
LinkedList内部结构是什么?**
双向链表结构
LinkedList有哪些特有方法?
addFirst
addLast
removeFirst
removeLast
getFirst
getLast
pop
push
代码实现!
人员类(Person):
|—学生类(Student)
|—Java学生类(JavaStudent)
|—UI学生类(UIStudent)
|—教师类(Teacher)
|—Java教师类(JavaTeacher)
|—UI教师类(UITeacher)
- 请按上述要求定义类,并实现继承关系即可(不需要定义类成员,定义空类表示关系即可)
- 请按以下格式和要求定义测试类和方法:
public class Test1{
public static void main(String[] args){
ArrayList list1 = new ArrayList<>();
ArrayList list2 = new ArrayList<>();
ArrayList list3 = new ArrayList<>();
ArrayList list4 = new ArrayList<>();
ArrayList list5 = new ArrayList<>();
ArrayList list6 = new ArrayList<>();
ArrayList list7 = new ArrayList<>();
ArrayList list8 = new ArrayList<>();
//请测试哪些集合可以调用print1()方法
//请测试哪些集合可以调用print2()方法
//请测试哪些集合可以调用print3()方法
//请测试哪些集合可以调用print4()方法
}
//要求:参数可以接收任何泛型的ArrayList参数
public static void print1(ArrayList<______________> list){
}
//要求:参数可以接收任何Person及其子类泛型的ArrayList参数
public static void print2(ArrayList<______________> list){
}
//要求:参数可以接收任何Student及其子类泛型的ArrayList参数
public static void print3(ArrayList<______________> list){
}
//要求:参数可以接收任何Java学员,及其父类泛型的ArrayList参数
public static void print4(ArrayList<______________> list){
}
}
//要求:参数可以接收任何泛型的ArrayList参数
public static void print1(ArrayList<?> list){
}
//要求:参数可以接收任何Person及其子类泛型的ArrayList参数
public static void print2(ArrayList<? extends Person> list){
}
//要求:参数可以接收任何Student及其子类泛型的ArrayList参数
public static void print3(ArrayList<? extends Student> list){
}
//要求:参数可以接收任何Java学员,及其父类泛型的ArrayList参数
public static void print4(ArrayList<? super Java> list){
}
```
请写出你所知道的数据结构,至少5种。
```java
栈, 队列 , 数组 , 链表 . 二叉树 , 哈希表
```
写出栈结构、队列结构的特点
```
栈: 先进后出 队列 : 先进先出
```
请写出数组结构的特点;
```
数组: 查询快 , 增删慢
```
请写出链表结构的特点
```
链表: 查询慢 , 增删快
```
需求:创建一个ArrayList集合,添加5个整数,然后使用不同的方式遍历这些数据。
1. 自增for循环遍历
2. 迭代器遍历
3. 增强for循环遍历
public static void main(String[] args) { ArrayListlist=new ArrayList<>(); list.add(40); list.add(50); list.add(70); list.add(90); list.add(100);** Integer []arr={40,50,70,90,100}; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } for (int a : arr) { System.out.println(“a = “ + a); } Iterator iter = list.iterator(); while (iter.hasNext()){ Integer next = iter.next(); System.out.println(“next = “ + next); } }}
需求:定义一个方法int listTest(ArrayList<Integer> list, Integer num)
,要求实现返回num在list里面第一次出现的索引,如果num没出现过返回-1。
思路:
- 在方法中遍历list集合,判断是否是num,如果是返回索引,遍历完成后没有发现数据直接返回-1
- 对象比较用equals- 在测试类中进行测试
public class Demo03 { public static void main(String[] args) { ArrayList list = new ArrayList<>(); list.add(40); list.add(50); list.add(60); list.add(70); list.add(80); System.out.println(listTest(list,5)); } public static int listTest(ArrayList list, Integer num) { for (int i = 0; i < list.size(); i++) { Integer integer = list.get(i); if (integer.equals(num)) { return i; } } return -1; }}**
1. 定义一个学生类Student,包含三个属性姓名、年龄、性别,私有成员变量,生成无参,有参构造方法,生成get/set方法。
2. 创建三个学生对象存入ArrayList集合中。
3. 找出年龄最大的学生,然后将该对象的姓名变为:小猪佩奇。
4. 遍历集合输出对象属性信息。(输出格式如:姓名:张三, 年龄:23, 性别:男)
public class Student {
private String name;
private int age;
private String sex;
public Student() {
}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public String toString() {
return “Student{“ +
“name=’” + name + ‘\’’ +
“, age=” + age +
“, sex=’” + sex + ‘\’’ +
‘}’;
}
}
public class StudentTest02 { public static void main(String[] args) { ArrayListS1=new ArrayList< >( ); Student01 s1=new Student01(“李四”,20,”男”); Student01 s2=new Student01(“和二”,26,”男”); Student01 s3=new Student01(“小花”,19,”女”); S1.add(s1); S1.add(s2); S1.add(s3); int max=0; Student01 maxSTU=S1.get(0); for (int i = 0; i < S1.size(); i++) { Student01 s=S1.get(i); if(s.getAge()>maxSTU.getAge()){ max= i; maxSTU=s; }} S1.get(max).setName(“小猪佩奇”); for (Student01 student01:S1){ System.out.println(“student01 = “ + student01); }}}
请编写main()方法,按以下要求顺序:
1. 定义一个Set集合,并存储以下数据:刘备,关羽,张飞,刘备,张飞
1. 打印集合大小
1. 使用迭代器遍历集合,并打印每个元素
1. 使用增强for遍历集合,并打印每个元素
思路:Set集合具有去重效果
public class Demo04 { public static void main(String[] args) { HashSeths=new HashSet<>(); hs.add(“刘备”); hs.add(“关羽”); hs.add(“张飞”); hs.add(“刘备”); hs.add(“张飞”); System.out.println(hs.size());** Iteratorit=hs.iterator(); while (it.hasNext()){ String s= it.next(); System.out.println(s); }** for (String h:hs){ System.out.println(h); }
请按以下要求顺序编码:
1. 定义一个可以存储“整数”的LinkedHashSet对象
2. 存储以下整数: 20,30,50,10,30,20
3. 打印集合大小。为什么跟存入的数量不一致?
4. 使用增强for遍历集合,打印大于25的元素
public class Test01 { public static void main(String[] args) { LinkedHashSet list = new LinkedHashSet<>(); list.add(20); list.add(30); list.add(50); list.add(10); list.add(30); list.add(20); System.out.println(list); for (Integer i : list) { if (i > 25) { System.out.println(“i = “ + i);
}}}}
为什么打印集合数量跟存入的数量不一致?
因为底层是哈希表结构+链表结构 , 哈希表保证元素唯一 , 链表保证元素顺序
day05 泛型,数据结构,List,Set
今日目标
1 泛型
1.1 泛型的介绍
- 泛型是一种类型参数,专门用来保存类型用的
- 最早接触泛型是在ArrayList,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被指定对应的类型
1.2 使用泛型的好处
- 不用泛型带来的问题
- 集合若不指定泛型,默认就是Object。存储的元素类型自动提升为Object类型。获取元素时得到的都是Object,若要调用特有方法需要转型,给我们编程带来麻烦.
- 使用泛型带来的好处
- 可以在编译时就对类型做判断,避免不必要的类型转换操作,精简代码,也避免了因为类型转换导致的代码异常
//泛型没有指定类型,默认就是Object
ArrayList list = new ArrayList();
list.add("Hello");
list.add("World");
list.add(100);
list.add(false);
//集合中的数据就比较混乱,会给获取数据带来麻烦
for (Object obj : list) {
String str = (String) obj;
//当遍历到非String类型数据,就会报异常出错
System.out.println(str + "长度为:" + str.length());
}
1.3 泛型的注意事项
- 泛型在代码运行时,泛型会被擦除。后面学习反射的时候,可以实现在代码运行的过程中添加其他类型的数据到集合
- 泛型只在编译时期限定数据的类型 , 在运行时期会被擦除
1.4 自定义泛型类
- 当一个类定义其属性的时候,不确定具体是什么类型时,就可以使用泛型表示该属性的类型
定义的格式
泛型的确定
- 当创建此泛型类是 , 确定泛型类中泛型的具体数据类型
练习
package com.itheima.genericity_demo.genericity_class;
import java.time.Period;
/*
需求 : 定义一个人类,定义一个属性表示爱好,但是具体爱好是什么不清楚,可能是游泳,乒乓,篮球。
*/
public class GenericityDemo {
public static void main(String[] args) {
Person<BasketBall> person = new Person<>();
person.setHobby(new BasketBall());
Person<Swim> person2 = new Person<>();
person2.setHobby(new Swim());
Person person3 = new Person<>();// 如果没有指定泛型 , 那么默认使用Object数据类型
}
}
class Person<H> {
// 定义属性表达爱好
private H hobby;
public H getHobby() {
return hobby;
}
public void setHobby(H hobby) {
this.hobby = hobby;
}
}
class Swim {
}
class PingPang {
}
class BasketBall {
}
1.3 自定义泛型接口
- 当定义接口时,内部方法中其参数类型,返回值类型不确定时,就可以使用泛型替代了。
定义泛型接口
泛型的确定
- 实现类去指定泛型接口的泛型
- 实现了不去指定泛型接口的泛型 , 进行延续泛型 , 回到泛型类的使用
package com.itheima.genericity_demo.genericity_interface;
/*
需求:
模拟一个Collection接口,表示集合,集合操作的数据不确定。
定义一个接口MyCollection具体表示。
*/
// 泛型接口
public interface MyCollection<E> {
// 添加功能
public abstract void add(E e);
// 删除功能
public abstract void remove(E e);
}
// 指定泛型的第一种方式 : 让实现类去指定接口的泛型
class MyCollectionImpl1 implements MyCollection<String>{
@Override
public void add(String s) {
}
@Override
public void remove(String s) {
}
}
// 指定泛型的第二种方式 : 实现类不确定泛型,延续泛型,回到泛型类的使用
class MyCollectionImpl2<E> implements MyCollection<E>{
@Override
public void add(E a) {
}
@Override
public void remove(E a) {
}
}
1.4 自定义泛型方法
- 当定义方法时,方法中参数类型,返回值类型不确定时,就可以使用泛型替代了
泛型方法的定义
- 可以在方法的返回值类型前定义泛型
- 格式 : public <泛型名> 返回值类型 方法名(参数列表){ … }
- 举例 : public void show(T t) { … }
泛型的确定
- 当调用一个泛型方法 , 传入的参数是什么类型, 那么泛型就会被确定
练习
```java
package com.itheima.genericity_demo.genericity_method;
import java.util.ArrayList;
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
// Collection集合中 : public T[] toArray(T[] a) : 把集合中的内容存储到一个数组中 , 进行返回
ArrayList list = new ArrayList<>();
list.add(“abc”);
list.add(“ads”);
list.add(“qwe”);
String[] array = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(array));
}
// 接收一个集合 , 往集合中添加三个待指定类型的元素
public static <X> void addElement(ArrayList<X> list, X x1, X x2, X x3) {
list.add(x1);
list.add(x2);
list.add(x3);
}
}
<a name="7e98fb7f"></a>
### 1.5 通配符
- 当我们对泛型的类型确定不了,而是表达的可以是任意类型,可以使用泛型通配符给定<br />符号就是一个问号:? 表示任意类型,用来给泛型指定的一种通配值。如下
```java
public static void shuffle(List<?> list){
//…
}
说明:该方法时来自工具类Collections中的一个方法,用来对存储任意类型数据的List集合进行乱序
- 泛型通配符结合集合使用
- 泛型通配符搭配集合使用一般在方法的参数中比较常见。在集合中泛型是不支持多态的,如果为了匹配任意类型,我们就会使用泛型通配符了。
- 方法中的参数是一个集合,集合如果携带了通配符,要特别注意如下
- 集合的类型会提升为Object类型
- 方法中的参数是一个集合,集合如果携带了通配符,那么此集合不能进行添加和修改操作 , 可以删除和获取
```java
package com.itheima.genericity_demo;
import java.util.ArrayList;
import java.util.List;
public class Demo {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add(“abc”);
list.add(“asd”);
list.add(“qwe”);
// 方法的参数是一个集合 , 集合的泛型是一个通配符 , 可以接受任意类型元素的集合
show(list);
}
public static void show(List<?> list) {
// 如果集合的泛型是一个通配符 , 那么集合中元素以Object类型存在
Object o = list.get(0);
// 如果集合的泛型是一个通配符 , 那么此集合不能进行添加和修改操作 , 可以删除和获取
// list.add(??);
// 删除可以
list.remove(0);
// 获取元素可以
for (Object o1 : list) {
System.out.println(o1);
}
}
}
```java
package com.itheima.genericity_demo;
import java.util.ArrayList;
/*
已知存在继承体系:Integer继承Number,Number继承Object。
定义一个方法,方法的参数是一个ArrayList。
要求可以接收ArrayList<Integer>,ArrayList<Number>,ArrayList<Object>,ArrayList<String>这些类型的数据。
结论 : 具体类型的集合,不支持多态 , 要想接收任意类型集合 , 需要使通配符集合
*/
public class Test1 {
public static void main(String[] args) {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<Number> list2 = new ArrayList<>();
ArrayList<String> list3 = new ArrayList<>();
ArrayList<Object> list4 = new ArrayList<>();
useList5(list1);
useList5(list2);
useList5(list3);
useList5(list4);
}
// 此方法只能接收存储Integer类型数据的集合
public static void useList1(ArrayList<Integer> list) {
}
// 此方法只能接收存储Number类型数据的集合
public static void useList2(ArrayList<Number> list) {
}
// 此方法只能接收存储String类型数据的集合
public static void useList3(ArrayList<String> list) {
}
// 此方法只能接收存储Object类型数据的集合
public static void useList4(ArrayList<Object> list) {
}
public static void useList5(ArrayList<?> list) {
}
}
1.6 受限泛型
- 受限泛型是指,在使用通配符的过程中 , 对泛型做了约束,给泛型指定类型时,只能是某个类型父类型或者子类型
- 分类 :
- 泛型的下限 :
- 泛型的上限 :
- //只能是某一个类型,及其子类型,其他类型不支持
```java
package com.itheima.genericity_demo.wildcard_demo;
import java.util.ArrayList;
/*
wildcardCharacter
基于上一个知识点,定义方法
show1方法,参数只接收元素类型是Number或者其父类型的集合
show2方法,参数只接收元素类型是Number或者其子类型的集合
*/
public class Test2 {
public static void main(String[] args) {
ArrayList list1 = new ArrayList<>();
ArrayList list2 = new ArrayList<>();
ArrayList