目录
泛型中的通配符
无界通配符
“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。
语法结构
public void showFlag(Generic<?> generic){ }
示例
public class Generic<T> {
private T flag;
public void setFlag(T flag){
this.flag = flag;
}
public T getFlag(){
return this.flag;
}
}
public class ShowMsg {
public void showFlag(Generic<?> generic){
System.out.println(generic.getFlag());
}
}
public class Test3 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
Generic<String> generic2 = new Generic<>();
generic2.setFlag("old");
showMsg.showFlag(generic2);
}
}
实时效果反馈
1.在泛型中,无界通配符使用什么符号来表示?
A !
B ?
C #
D *答案
1=>B
统配符的上下限定
统配符的上限限定
对通配符的上限的限定:<? extends 类型> ?实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;
语法结构
public void showFlag(Generic<? extends Number> generic){ }
示例
public class ShowMsg {
public void showFlag(Generic<? extends Number> generic){
System.out.println(generic.getFlag());
}
}
public class Test4 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
}
}
实时效果反馈
1.对通配符的上限的限定是指
A 实际类型只能是上限限定中所约定的类型;
B 实际类型只能是上限限定中所约定类型的子类型;C 实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是上限限定中所约定的类型,也可以是约定类型的父类型;答案
1=>C
通配符的下限限定
对通配符的下限的限定:<? super 类型> ?实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;
语法结构
public void showFlag(Generic<? super Integer> generic){ }
示例
public class ShowMsg {
public void showFlag(Generic<? super Integer> generic){
System.out.println(generic.getFlag());
}
}
public class Test6 {
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(20);
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50);
showMsg.showFlag(generic1);
}
}
实时效果反馈
1.对通配符的下限的限定是指
A 实际类型只能是下限限定中所约定的类型;
B 实际类型只能是下限限定中所约定类型的子类型;
C 实际类型可以是下限限定中所约定的类型,也可以是约定类型的子类型;
D 实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;答案
1=>D
泛型局限性和常见错误
泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息。 类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。因此,使用泛型时,如下几种情况是错的:
实时效果反馈
1.如下哪个选项是错误的使用泛型?
A Generic
B Generic
C Generic
D Generic答案
1=>D
二、容器介绍
容器简介
容器,是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等。
程序中的“容器”也有类似的功能,用来容纳和管理数据。比如,如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理:
开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。 我们一般通过“容器”来容纳和管理数据。事实上,我们前面所学的数组就是一种容器,可以在其中
放置对象或基本类型数据。
数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从查询效率和类型检查的角度讲,数组是最好的。
数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。
基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。
实时效果反馈
1.Java中容器的作用是什么?
A 容纳数据
B 处理数据
C 生产数据
D 销毁数据答案
1=>A
容器的结构
结构图
单例集合
双例集合
实时效果反馈
1.如下哪个接口不是容器接口?
A List
B Set
C Map
D Comparable答案
1=>D
单例集合
Collection接口介绍
Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。
Collection接口中定义的方法
方法 | 说明 |
boolean add(Object element) | 增加元素到容器中 |
boolean remove(Object element) | 从容器中移除元素 |
boolean contains(Object element) | 容器中是否包含该元素 |
int size() | 容器中元素的数量 |
boolean isEmpty() | 容器是否为空 |
void clear() | 清空容器中所有元素 |
Iterator iterator() | 获得迭代器,用于遍历所有元素 |
boolean containsAll(Collection c) | 本容器是否包含c容器中的所有元素 |
boolean addAll(Collection c) | 将容器c中所有元素增加到本容器 |
boolean removeAll(Collection c) | 移除本容器和容器c中都包含的元素 |
boolean retainAll(Collection c) | 取本容器和容器c中都包含的元素,移除非交集元素 |
Object[] toArray() | 转化成Object数组 |
由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。
JDK8之后,Collection接口新增的方法(将在JDK新特性和函数式编程中介绍):
新增方法 说明 removeIf 作用是删除容器中所有满足filter指定条件的元素 stream
parallelStreamstream和parallelStream 分别返回该容器的Stream视图表示,不同之处在于parallelStream()返回并行的Stream,Stream是Java函数式编程的核心类 spliterator 可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,可以实现多线程操作提高效率
实时效果反馈
1.如下哪个接口不是Collection接口的子接口?
A List
B Set
C Map
D Queue答案
1=>C
List接口介绍
List接口特点
List是有序、可重复的容器。
有序:有序(元素存入集合的顺序和取出的顺序一致)。List中每个元 素都有索引标记。可以根据元素的索引标记(在List中的位置)访问 元素,从而精确控制这些元素。
可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。
List接口中的常用方法
除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方 法,参见下表:
ArrayList容器的基本使用
ArrayList容器的基本使用
ArrayList是List接口的实现类。是List存储特征的具体实现。 ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率 低,线程不安全。
public class ArrayListTest {
public static void main(String[] args) {
//实例化ArrayList容器
List<String> list = new ArrayList<>();
//添加元素
boolean flag1 = list.add("oldlu");
boolean flag2 = list.add("itbz");
boolean flag3 = list.add("sxt");
boolean flag4 = list.add("sxt");
System.out.println(flag1+"\t"+flag2+"\t"+flag3+"\t"+flag4);
//删除元素
boolean flag4 = list.remove("oldlu");
System.out.println(flag4);
//获取容器中元素的个数
int size = list.size();
System.out.println(size);
//判断容器是否为空
boolean empty = list.isEmpty();
System.out.println(empty);
//容器中是否包含指定的元素
boolean value = list.contains("itbz");
System.out.println(value);
//清空容器
list.clear();
Object[] objects1 = list.toArray();
System.out.println(Arrays.toString(objects1));
}
}
ArrayList容器的索引操作
public class ArrayListTest2 {
public static void main(String[] args) {
//实例化容器
List<String> list = new ArrayList<>();
//添加元素
list.add("oldlu");
list.add("itbz");
//向指定位置添加元素
list.add(0,"sxt");
System.out.println("获取元素");
String value1 = list.get(0);
System.out.println(value1);
System.out.println("获取所有元素方式一");
//使用普通for循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
System.out.println("获取所有元素方式二");
//使用Foreach循环
for(String str:list){
System.out.println(str);
}
System.out.println("元素替换");
list.set(1,"kevin");
for(String str:list){
System.out.println(str);
}
System.out.println("根据索引位置删除元素);
String value2 = list.remove(1);
System.out.println(value2);
System.out.println("----------------");
for(String str:list){
System.out.println(str);
}
System.out.println("查找元素第一次出现的位置");
int value3 = list.indexOf("sxt");
System.out.println(value3);
System.out.println("查找元素最后一次出现的位置");
list.add("sxt");
for(String str:list){
System.out.println(str);
}
int value4 = list.lastIndexOf("sxt");
System.out.println(value4);
}
}
ArrayList的并集、交集、差集
并集
//并集操作:将另一个容器中的元素添加到当前容器中
List<String> a = new ArrayList<>();
a.add("a");
a.add("b");
a.add("c");
List<String> b = new ArrayList<>();
b.add("a");
b.add("b");
b.add("c");
//a并集b
a.addAll(b);
for(String str :a){
System.out.println(str);
}
交集
//交集操作:保留相同的,删除不同的
List<String> a1 = new ArrayList<>();
a1.add("a");
a1.add("b");
a1.add("c");
List<String> b1 = new ArrayList<>();
b1.add("a");
b1.add("d");
b1.add("e");
//交集操作
a1.retainAll(b1);
for(String str :a1){
System.out.println(str);
}
差集
//差集操作:保留不同的,删除相同的
List<String> a2 = new ArrayList<>();
a2.add("a");
a2.add("b");
a2.add("c");
List<String> b2= new ArrayList<>();
b2.add("b");
b2.add("c");
b2.add("d");
a2.removeAll(b2);
for(String str :a2){
System.out.println(str);
}
ArrayList源码分析
ArrayList底层是用数组实现的存储。
成员变量
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* The array buffer into which the elements of the ArrayList are stored.
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*/
transient Object[] elementData; // nonprivate to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*/
private int size;
数组初始大小
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
添加元素
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); //Increments modCount!!
elementData[size++] = e;
return true;
}
判断数组是否扩容
//容量检查
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//容量确认
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
数组扩容
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData,newCapacity);
}
Vector容器的基本使用
Vector底层是用数组实现的,相关的方法都加了同步检查,因此“线 程安全,效率低”。 比如,indexOf方法就增加了synchronized同步 标记。
Vector的使用
Vector的使用与ArrayList是相同的,因为他们都实现了List接口, 对List接口中的抽象方法做了具体实现。
public class VectorTest {
public static void main(String[] args) {
//实例化Vector
List<String> v = new Vector<>();
v.add("a");
v.add("b");
v.add("a");
for(int i=0;i<v.size();i++){
System.out.println(v.get(i));
}
System.out.println("----------------------");
for(String str:v){
System.out.println(str);
}
}
}
Vector源码分析
成员变量
/**
* The array buffer into which the components of the vector are
* stored. The capacity of the vector is the length of this array buffer,
* and is at least large enough to contain all the vector's elements.
*
* <p>Any array elements following the last element in the Vector are null.
*
* @serial
*/
protected Object[] elementData;
/**
* The number of valid components in this {@code Vector} object.
* Components {@code elementData[0]} through
* {@code elementData[elementCount-1]} are the actual items.
*
* @serial
*/
protected int elementCount;
/**
* The amount by which the capacity of the vector is automatically
* incremented when its size becomes greater than its capacity. If
* the capacity increment is less than or equal to zero, the capacity
* of the vector is doubled each time it needs to grow.
*
* @serial
*/
protected int capacityIncrement;
构造方法
public Vector() {
this(10);
}
添加元素
/**
* Appends the specified element to the end of this Vector.
*
* @param e element to be appended to this Vector
* @return {@code true} (as specified by {@link Collection#add})
* @since 1.2
*/
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
数组扩容
/**
* This implements the unsynchronized semantics of ensureCapacity.
* Synchronized methods in this class can internally call this
* method for ensuring capacity without incurring the cost of an
* extra synchronization.
*
* @see #ensureCapacity(int)
*/
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
//判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
if (minCapacity - elementData.length >0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容2倍
int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}