1、泛型Generics
开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。 我们一般通过“容器”来容纳和管理数据。那什么是“容器”呢?生活中的容器不难理解,是用来容纳物体的,如锅碗瓢盆、箱子和包等。程序中的“容器”也有类似的功能,就是用来容纳和管理数据。
事实上,我们第七章所学的数组就是一种容器,可以在其中放置对象或基本类型数据。
数组的优势:
是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。
数组的劣势:
不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的
所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。
基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。以下是容器的接口层次结构图:
为了能够更好的学习容器,我们首先要先来学习一个概念:泛型。
泛型是JDK1.5以后增加的,它可以帮助我们建立类型安全的集合。在使用了泛型的集合中,遍历时不必进行强制
类型转换。JDK提供了支持泛型的编译器,将运行时的类型检查提前到了编译时执行,提高了代码可读性和安全性。
泛型的本质就是“数据类型的参数化”。 我们可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。
泛型就相当于给容器贴上标签,可以帮助我们建立类型安全的容器(或集合)。
(要点)泛型的作用:
数组里存放的是同类型的变量;而容器是广义的数组,里面存放的是不同类型的变量,所以需要一个参数定义类型。
简单来说:《泛型就是用来规范数组里存放数据的数据类型的》。
1.1 自定义泛型
我们可以在类的声明处增加泛型列表,如:<T,E,V>。
此处,字符可以是任何标识符,一般采用这3个字母。
package com.gk.collection;
/**
* 测试泛型
* @author 康哥
*
*/
public class TestGeneric {
public static void main(String[] args) {
MyCollection<String> mc = new MyCollection<String>();
mc.set("高明", 0);
String str = mc.get(0);
System.out.println(str);
}
}
class MyCollection<E>{
Object[] objs = new Object[5];
public void set(E e, int index) {
objs[index] = e;
}
public E get(int index) {
return (E) objs[index];
}
}
1.2容器中使用的泛型
容器相关类都定义了泛型,我们在开发和工作中,在使用容器类时都要使用泛型。这样,在容器的存储数据、读取数据时都避免了大量的类型判断,非常便捷。
public class Test {
public static void main(String[] args) {
// 以下代码中List、Set、Map、Iterator都是与容器相关的接口;
List<String> list = new ArrayList<String>();
Set<Man> mans = new HashSet<Man>();
Map<Integer, Man> maps = new HashMap<Integer, Man>();
Iterator<Man> iterator = mans.iterator();
}
}
2、Collection接口
Collection与List的详解:
点击这
Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。
例子:
package com.gk.collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 测试Collection接口中的方法
* Collection、List、Set基本都是这些方法
* @author 康哥
*
*/
public class TestList {
public static void main(String[] args) {
//test01();
test02();
}
public static void test01() {
Collection<String> c = new ArrayList<>();
System.out.println(c.size());
c.add("高老大");
c.add("高老二");
System.out.println(c.size());
System.out.println(c.isEmpty());
System.out.println(c);
c.remove("高老二");//这里移除的是"高老二"对象的索引,"高老二"对象并未删除。
System.out.println(c);
System.out.println(c.contains("高老三"));//c中是否包含"高老三"。
c.clear();//移除所有元素
System.out.println(c);
}
public static void test02() {
//这里的List也可以用Collection替换(List和Set是Collection的子接口)
List<String> list01 = new ArrayList<>();
list01.add("aa");
list01.add("bb");
list01.add("cc");
List<String> list02 = new ArrayList<>();
list02.add("aa");
list02.add("dd");
list02.add("ee");
System.out.println("list01:" + list01);
//list01.addAll(list02);//把list02中的所有元素添加到list01中
//list01.removeAll(list02);//移除list01中和list02共有的元素
list01.retainAll(list02);//list01和list02取交集
System.out.println("list01:" + list01);
}
}
test01();的运行结果:
test02();的运行结果自己测试。
3.1List特点和常用方法
List是有序、可重复的容器。
有序:List中每个元素都有索引标记。可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。
除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方法,参见下表:
List接口常用的实现类:ArrayList(查询效率高)、LinkedList(增删效率高)、Vector(线程安全)。
例子:
//测试跟索引相关的方法
public static void test03() {
List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
System.out.println(list);
list.add(2, "高明");//相当于插入
System.out.println(list);
list.set(3, "高明");//相当于修改
System.out.println(list);
System.out.println(list.get(2));//返回索引处的元素
list.add("C");
list.add("B");
list.add("A");
System.out.println(list.indexOf("B"));//返回第一个"B"的索引
System.out.println(list.lastIndexOf("B"));//返回最后一个"B"的索引
}
运行结果:
3.2ArrayList特点和底层实现
ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率低,线程不安全。我们一般使用它。查看源码:
我们可以看出ArrayList底层使用Object数组来存储元素数据。所有的方法,都围绕这个核心的Object数组来开展。
我们知道,数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制,那么它是怎么实现的呢?本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 ArrayList的Object数组初始化长度为10,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中,源码如下:
package com.gk.mycollection;
import java.util.Arrays;
/**
* 自定义实现一个ArrayList,体会底层原理
* @author 康哥
*
*/
public class SxtArrayList01 {
private Object[] elementDate;
private int size;
private static final int DEFAULT_CAPACITY = 10;
public SxtArrayList01() {
elementDate = new Object[DEFAULT_CAPACITY];
}
public SxtArrayList01(int capacity) {//capacity:容量
elementDate = new Object[capacity];
}
public void add(Object obj) {
elementDate[size++] = obj;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < size; i++) {
sb.append(elementDate[i] + ",");
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtArrayList01 s1 = new SxtArrayList01(12);
s1.add("aa");
s1.add("bb");
System.out.println(s1);
}
}
package com.gk.mycollection;
import java.util.Arrays;
/**
* 自定义实现一个ArrayList,体会底层原理
* 增加泛型
* @author 康哥
*
*/
public class SxtArrayList02<E> {
private Object[] elementDate;
private int size;
private static final int DEFAULT_CAPACITY = 10;
public SxtArrayList02() {
elementDate = new Object[DEFAULT_CAPACITY];
}
public SxtArrayList02(int capacity) {//capacity:容量
elementDate = new Object[capacity];
}
public void add(E element) {
elementDate[size++] = element;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < size; i++) {
sb.append(elementDate[i] + ",");
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtArrayList02 s1 = new SxtArrayList02(12);
s1.add("aa");
s1.add("bb");
System.out.println(s1);
}
}
package com.gk.mycollection;
import java.util.Arrays;
/**
* 增加数组扩容
* @author 康哥
*
*/
public class SxtArrayList03<E> {
private Object[] elementDate;
private int size;
private static final int DEFAULT_CAPACITY = 10;
public SxtArrayList03() {
elementDate = new Object[DEFAULT_CAPACITY];
}
public SxtArrayList03(int capacity) {//capacity:容量
elementDate = new Object[capacity];
}
public void add(E element) {
//什么时候需要扩容?
if(size == elementDate.length) {
//怎么扩容
Object[] newArray = new Object[elementDate.length + (elementDate.length >> 1)];//扩容一半; "+"的运算优先级大于">>"的优先级,所以加括号;10-->10+10/2
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);//把数组elementDate拷贝到newArray中
elementDate = newArray;
}
elementDate[size++] = element;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < size; i++) {
sb.append(elementDate[i] + ",");
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtArrayList03 s1 = new SxtArrayList03();
for(int i = 0; i < 30; i++) {
s1.add("gao" + i);
}
System.out.println(s1);
}
}
package com.gk.mycollection;
import java.util.Arrays;
/**
* 增加get、set方法,数组索引越界检查
* @author 康哥
*
*/
public class SxtArrayList04<E> {
private Object[] elementDate;
private int size;
private static final int DEFAULT_CAPACITY = 10;//容器的默认容量
public SxtArrayList04() {
elementDate = new Object[DEFAULT_CAPACITY];
}
public SxtArrayList04(int capacity) {//capacity:容量
if(capacity < 0) {
throw new RuntimeException("容器的容量不可能为负!");
}else if(capacity == 0){
elementDate = new Object[DEFAULT_CAPACITY];
}else {
elementDate = new Object[capacity];
}
}
public void add(E element) {
//什么时候需要扩容?
if(size == elementDate.length) {
//怎么扩容
Object[] newArray = new Object[elementDate.length + (elementDate.length >> 1)];//扩容一半; "+"的运算优先级大于">>"的优先级,所以加括号;10-->10+10/2
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);//把数组elementDate拷贝到newArray中
elementDate = newArray;
}
elementDate[size++] = element;
}
public E get(int index) {
checkRange(index);
return (E) elementDate[index];
}
public void set(E element, int index) {
checkRange(index);
elementDate[index] = element;
}
//索引合法判断
public void checkRange(int index) {
//索引合法判断[0,size)
if(index < 0 || index > size - 1) {
throw new RuntimeException("索引越界:" + index);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < size; i++) {
sb.append(elementDate[i] + ",");
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtArrayList04 s1 = new SxtArrayList04(10);
for(int i = 0; i < 30; i++) {
s1.add("gao" + i);
}
s1.set("Gao", 20);
System.out.println(s1);
System.out.println(s1.get(20));
}
}
package com.gk.mycollection;
import java.util.Arrays;
/**
* 增加remove方法,size(),isEmpty()方法
* @author 康哥
*
*/
public class SxtArrayList05<E> {
private Object[] elementDate;
private int size;//数组里元素的个数
private static final int DEFAULT_CAPACITY = 10;//容器的默认容量
public SxtArrayList05() {
elementDate = new Object[DEFAULT_CAPACITY];
}
public SxtArrayList05(int capacity) {//capacity:容量
if(capacity < 0) {
throw new RuntimeException("容器的容量不可能为负!");
}else if(capacity == 0){
elementDate = new Object[DEFAULT_CAPACITY];
}else {
elementDate = new Object[capacity];
}
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0 ? true : false;
}
public void add(E element) {
//什么时候需要扩容?
if(size == elementDate.length) {
//怎么扩容
Object[] newArray = new Object[elementDate.length + (elementDate.length >> 1)];//扩容一半; "+"的运算优先级大于">>"的优先级,所以加括号;10-->10+10/2
System.arraycopy(elementDate, 0, newArray, 0, elementDate.length);//把数组elementDate拷贝到newArray中
elementDate = newArray;
}
elementDate[size++] = element;
}
public E get(int index) {
checkRange(index);
return (E) elementDate[index];
}
public void set(E element, int index) {
checkRange(index);
elementDate[index] = element;
}
//索引合法判断
public void checkRange(int index) {
//索引合法判断[0,size)
if(index < 0 || index > size - 1) {
throw new RuntimeException("索引越界:" + index);
}
}
//通过元素移除
public void remove(E element) {
//element,将它和所有的元素挨个比较,获得第一个比较为true的返回
for(int i = 0; i < size; i++) {
if(element.equals(get(i))){
remove(i);//调用索引移除的方法
}
}
}
//通过索引移除
public void remove(int index) {
int numMoved = size - 1 - index;
if(numMoved > 0) {
//通过索引移除实际上还是数组的拷贝
System.arraycopy(elementDate, index + 1, elementDate, index, numMoved);
}
elementDate[--size] = null;//移除某个元素后 size-1(数组元素个数减1),且最后一个索引位置设置为空
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < size; i++) {
sb.append(elementDate[i] + ",");
}
sb.setCharAt(sb.length() - 1, ']');
return sb.toString();
}
public static void main(String[] args) {
SxtArrayList05 s1 = new SxtArrayList05(10);
for(int i = 0; i < 30; i++) {
s1.add("gao" + i);
}
s1.set("Gao", 20);
System.out.println(s1);
System.out.println(s1.get(20));
s1.remove(3);
System.out.println(s1);
s1.remove("gao0");
System.out.println(s1);
System.out.println(s1.size());
System.out.println(s1.isEmpty());
}
}