- 泛型的通配符?
- 泛型的上下限
- 简单数据结构
- List
- Set
- Collections类(Comparator接口、Comparable接口)
- 了解泛型通配符
- 理解泛型上下限
- 表达泛型通配符的作用
- List集合的特点
- 常见的数据结构
- 队列结构特点
- 单项链表结构特点
- Set集合特点
- 哈希表的特点
- HashSet集合存储自定义元素
- 说出可变参数的格式
- 使用集合工具类
- 使用Comparator比较器进行排序
泛型的通配符
当使用泛型类(接口)传递的数据中,泛型的类型不确定,可以通过通配符<?>表示,一旦在程序中使用泛型通配符之后,只能使用Object类中的共性方法。集合中元素自身方法无法使用。
通配符的基本使用
泛型的通配符:**不知道使用什么类型来接收的时候,此时可以使用 ?,?代表未知的通配符 **
此时,只能接收数据,不能往该集合当中存储数据
示例:
public static void main(String []args){
//可以存储整数的集合
Collection<Integer> list01 = new ArrayList();
//此时list01可以存储整数数据
//展示list01集合当中的数据
getElement(list01);
//可以存储String字符串的集合
Collection<String> list02 = new ArrayList();
//此时list02可以存储字符串数据
getElement(list02); // Collection<Object> × 错误写法 Collection<?> √ 正确
}
//
public static void getElement(Collection<Object> coll){
//只能接受Integer类型数据
}
public static void getElement2(Collection<?> coll){
//能接受任意类型数据
}
备注:泛型不存在继承关系,Collection<Object> list = new ArrayList<String>(); ×写法
既然想要接受多种类型的泛型 ,Object又不能被其他泛型继承
选择使用通配符
通配符的高级用法---受限泛型
之前设置泛型的时候,实际上是可以任意设置的。只要是类就可以,但是在Java的泛型当中还可以指定一个上限和下限
泛型的上限
- 格式: 类型名称< ? extends 类名> 对象名称
作用:只能接收该类型及其子类
泛型的下限
- 格式:类型名称<? super 类名> 对象名称
作用:只能接收该类型及其父类类型
比如说 :已知的顶级父类Object、String类、Number类、Integer类,其中Number类是Integer的父类
示例:
public static void main(String []args){
//初始化三个集合
Collection<Integer> list01 = new ArrayList();
Collection<String> list02 = new ArrayList();
Collection<Number> list03 = new ArrayList();
Collection<Object> list04 = new ArrayList();
}
//定义方法 此时可以接收任意数据类型
public static void getElement(Collection<?> coll){}
//定义方法 此时只可以接收数字类型
public static void getElement01(Collection<? extends Number> coll){}
//定义方法 此时只可以接收数字类型及其以上的类型
public static void getElement02(Collection<? super Number> coll){}
数据类型
数据结构有什么作用
当我们使用着Java官方给你提供的容器的时候,用起来体验是很好的,ArrayList其实就是一个无限扩充的数组,LinkedList其实是一个链表。
现实世界中存储数据,我们要通过一些工具或者建模来进行存储,每种存储容器都有自己的特点。算法就是用来优化种种对数据的操作的思想。
常见的数据结构:堆栈 、队列、数组、链表、红黑树
数据存储的常用结构:栈、队列、数组、链表、红黑树
栈
又称堆栈,stack,是一种运算受限的线性表结构,仅允许在标的的一端进行插入和删除操作,不允许在其他任何位置进行添加、查找、删除等操作。
简单来说:采用该结构的集合,对元素的存取有以下特点:
- 先进后出(存进去的元素,要在他后面的元素依次取出后,才能取出该元素)
- 栈的入口、出口都是栈的顶端位置
队列
queue简称队,也是一种运算受限的线性表结构,仅允许在标的的一端进行插入,在标的的另一端进行删除
简单来说:采用该结构的集合,对元素的存取有以下特点:
- 先进先出(存进去的元素,要在他的前面依次取出后才能取出)
- 队列的入口、出口各占一侧
数组
array,是有序的元素序列,在内存当中开辟的是一段连续的空间,并在此空间内存储元素。
简单来说:采用该结构的集合,对元素的存取有以下特点:
- 查找元素快,通过索引,可以快速的访问到指定位置的元素
- 增删速率低:1.指定索引位置增删元素 :需要创建一个新的数组 ,将指定的新元素存储到指定的索引位置,再把源数组元素根据原来的索引,复制到新数组对应的索引位置 2. 指定索引位置删除元素:需要创建一个新数组,把源数组当中的元素根据索引,复制到新数组对应的索引位置,源数组中指定的索引位置元素不复制到新数组中。
链表结构
linked list,由一系列结点(node,链表当中的每个元素称为结点)组成,结点可以在运行时动态生成,每个结点包含两个部分:一个用于存储数据元素的数据域,另一个是用于存储下一个结点地址的指针域。常说的链表结构有单向链表、双向链表
单向链表:链表中只有一条链,不能保证元素的存取顺序
双向链表:其中有一条链专门记录元素顺序,是有序的存储结构
简单来说:采用该结构的集合,对元素的存取有以下特点:
- 查询慢,每次查询都必须从头开始通过链接的结点,但是增删极快
红黑树
特点:本身是一个二叉树,趋近于二叉平衡树,查询的速度非常快,查询叶子节点最大次数数和最小次数数不能超过2倍
红黑树的约束:
- 结点可以是黑色的或红色的
- 根节点必须是黑色的
- 叶子节点是黑色的
- 每个红色节点的子节点是黑色的
- 任何一个节点到其每一个叶子节点的所有路径上的黑色节点数目相同
关于二叉树:binary tree,每个节点上的子节点数不超过2的树(tree)
根节点、分支节点、叶子节点、左子树、右子树
List集合
List接口:java.util.List
继承自Collection接口,是单列集合的一个重要分支,在List集合之中允许出现重复的元素,所有的元素都是以一种线性方式进行存储的。在List集合当中,我们可以通过索引来进行元素的访问。另外List集合的元素是有序的(存取元素顺序相同)。
List接口当中的常用API方法:
除了继承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集合的子类
ArrayList集合:有索引、有序、元素可重复、长度可变的数组、可存null、查询快、但增删慢、实现不同步,线程不安全
LinkedList集合:java.util.LinkedList
,存储采用链表结构,方便元素的添加和删除操作
此外【LinkedList】是一个双向链表,元素有序、增删快、查询慢。在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():从此列表所表示的堆栈中弹出一个元素
public void push(E e):将元素推入到此列表所表示的堆栈当中
public boolean isEmpty():判断是否为空
List当中的方法LinkedList都是可以使用的。这里只介绍其独有的方法,在开发当中,LinkedList集合也可以作为堆栈、队列结构使用。
Vector集合
Set接口
java.util.Set
接口和java.util.List
接口是一样的,都是继承自Collection接口,它与Collection接口中的方法基本一样,没有对Collection接口进行功能上的拓展,只是比Collection接口更加严格,与List接口不同的是,Set接口中的元素是无序的,并且都会以某种规则保证存入的元素不重复。
Set接口有很多子类,我们主要介绍两个重要的子类:java.util.HashSet
集合和java.util.LikedHashSet
集合
Set集合取出元素的方式:迭代器、增强型for
HashSet集合
java.util.HashSet
是Set接口的实现类,它存储的元素不可重复,元素无序,其底层的实现是一个java.util.HashMap(哈希表)
支持的。
HashSet
是根据对象的哈希值来确定元素在集合当中的存储位置,因此它具有良好的存取和查找性能。保证元素的唯一性,依赖于重写了Object类中的hasCode
和equals
方法
哈希表:
JDK1.8之前 : 数组 + 链表
JDK1.8之后 :数组 + 链表 / 数组 + 红黑树
如果链表的长度超过了阈值(8位),那么就会把链表结构转换为红黑树结构
存储数据到集合当中,先计算该元素的哈希值,根据哈希值对元素进行分类。(相同成组)
链表/红黑树结构把相同的哈希值元素挂到一起(像风铃一样)
两个元素不同,但哈希值相同(哈希冲突)
Set如何保证元素的唯一性
set集合在调用add方法的时候,add方法会调用元素的hashCode方法和equals方法,并判断元素是否重复。hashCode不重复则添加元素,如果hashCode相同,则调用equals方法,返回true,则不添加元素到集合如果我们往集合当中存储的是自定义的对象,需要保证对象的唯一性,就必须重写hashCode和equals方法,来自定义当前对象的比较方式
HashSet存储自定义类型的元素
一般需要重写对象当中的hashCode和equals方法,建立自己的比较方式,才能保证HashSet集合中元素的唯一性。
LinkedHashSet集合
我们知道HashSet保证元素的唯一,可是存进去的元素是没有顺序的,那么如何保证存进去的元素是有序的呢?
在java.util.HashSet
类的下面还有一个子类java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构
可变参数
在JDK1.5之后,如果我们定义一个方法需要接收多个参数,并且多个参数的数据类型一致,那么我们可以简化成如下格式:
修饰符 返回值类型 方法名(数据类型... 形参名){
//...
}
其实上面的格式完全等价于下面的格式:
修饰符 返回值类型 方法名(参数类型[] 形参名){
//...
}
只是后面的写法,在方法调用的时候,必须传递一个数组类型,而前者可以直接传递参数数据。
JDK1.5之后,出现的这种简化操作 " ... " 用在参数上,我们称之为可变参数。
同样是代表数组,但是在方法调用时,带有可变参数列表时,无需创建数组,直接将数组当中的元素做为实际参数进行传递,其实编译生成的class文件,本质是将这些元素封装到了一个数组中,再进行数据传递,这些动作都在编译生成class文件时就自动完成了
Collections集合工具类
java.util.Collections
是集合工具类,用来操作集合对象中的元素,方法如下:
- public static
boolean addAll(Collection<? super T> c,T...elements):往集合中一次性添加多个元素 - public static
void shuffle(List list):打乱集合元素顺序 - public static
void sort(List list):将集合元素按默认规则排序 - public static
void sort(List list,comparator <? super T> c):将集合元素按照指定规则进行排序
sort(List
示例:
@Override
public int compareTo(Student stu){
//自定义排序规则 按年龄进行排序
//升序排序
return this.getAge() - stu.getAge();
//降序排序stu.getAge() - this.getAge()
}