文章目录
1、泛型
Java声明方法时,当在完成方法功能时如果有未知的数据需要参与,这些未知的数据需要在调用方法时才能确定,那么我们把这样的数据通过形参表示。那么在方法体中,用这个形参名来代表那个未知的数据,而调用者在调用时,对应的传入值就可以了
于是JDK1.5设计了泛型的概念
泛型即为“类型参数”,这个类型参数在声明它的类、接口或方法中,代表未知的通用的类型
java.util.Comparator
是用于对象比较大小的规范接口,在过去,不确定对象是什么类型,只能用Object类型表示,因此JDK1.5就给它们增加了泛型,示例如下,留意<T>
import java.util.Comparator;
class CircleComparator1 implements Comparator {
@Override
public int compare(Object o1, Object o2) {
//强制类型转换
Circle c1 = (Circle) o1;
Circle c2 = (Circle) o2;
return Double.compare(c1.radius, c2.radius);
}
}
class CircleComparator2 implements Comparator<Circle> {
@Override
public int compare(Circle o1, Circle o2) {
// 不需要强制类型转换,代码更简洁
return Double.compare(o1.radius, o2.radius);
}
}
class Circle {
double radius;
Circle(double radius) {
this.radius = radius;
}
}
public class Hello {
public static void main(String[] args) {
// 不使用泛型
CircleComparator1 c1 = new CircleComparator1();
System.out.println(c1.compare(new Circle(1), new Circle(2)));
System.out.println(c1.compare("圆1", "圆2")); // 编译不报错,运行时ClassCastException
// 使用泛型
CircleComparator2 c2 = new CircleComparator2();
System.out.println(c2.compare(new Circle(1), new Circle(2)));
System.out.println(c2.compare("圆1", "圆2")); // 编译报错,而不是冒着风险在运行时再报错
}
}
1.1、泛型类与泛型接口
语法格式:
class 类名<类型变量列表>{}
interface 接口名<类型变量列表>{}
<类型变量列表>
:可以是n个类型变量,一般用单个的大写字母,如:<T>
、<K,V>
…<类型变量列表>
中的类型变量不能用于静态成员上。
场景:
学生类包含姓名和成绩,而此时学生的成绩类型不确定
因为语文老师希望成绩是【“优秀”、“良好”、“及格”、“不及格”】
数学老师希望成绩是【89、65】
英语老师希望成绩是【‘A’,‘B’,‘C’,‘D’,‘E’】
那么我们在设计这个学生类时就可以使用泛型
class Student<T> {
String name;
T score;
public Student(String name, T score) {
this.name = name;
this.score = score;
}
}
测试学生类
1.1.1、继承泛型类
class Student<T> {
String name;
T score;
public Student(String name, T score) {
super();
this.name = name;
this.score = score;
}
}
class Pupil extends Student<Double> {
public Pupil(String name, Double score) {
super(name, score);
}
}
public class Hello {
public static void main(String[] args) {
Pupil p = new Pupil("小黄", 99.5);
}
}
1.1.2、泛型设定上限
语法格式:
<类型变量 extends 上限>
<类型变量 extends 上限1 & 上限2>
示例:
限定只能为数字类
1.1.3、泛型擦除
当使用参数化类型的类或接口时,如果没有指定泛型,会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object
1.2、泛型方法
什么情况需要声明泛型方法?
(情况一)如果某个静态方法想要使用泛型,需要单独设计
例如:java.util.Arrays
的public static <T> List<T> asList(T... a)
(情况二)如果泛型类或泛型接口上的泛型形参不适用于某一个方法,该方法静态非静态都可
例如:java.util.Collection<E>
的public abstract <T> T[] toArray(T[] a)
语法格式:
【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{
//...
}
1.3、类型通配符
<?>
:代表可以是任意类型<? extends 上限>
:代表上限或上限的子类<? super 下限>
:代表下限或下限的父类
import java.util.ArrayList;
class Animal {}
class Cat extends Animal {}
class Tiger extends Cat {}
public class Hello {
public static void main(String[] args) {
ArrayList<Animal> animals = new ArrayList<>();
ArrayList<Cat> cats = new ArrayList<>();
ArrayList<Tiger> tigers = new ArrayList<>();
Hello hello = new Hello();
//
hello.print3(animals);
hello.print3(tigers);
//
hello.print4(animals);
hello.print4(tigers);
}
public void print1(ArrayList arrayList) {}
public void print2(ArrayList<?> arrayList) {}
public void print3(ArrayList<? extends Cat> arrayList) {}
public void print4(ArrayList<? super Cat> arrayList) {}
}
2、自写一个动态数组
import java.util.Arrays;
public class MyArrayList {
private Object[] container; //存储对象的数组
private int size; //实际元素的个数
public MyArrayList() {
container = new Object[5];
}
//数组容量
public int capacity() {
return container.length;
}
//检查container是否需要扩容,如果满了就扩容为原来的2倍
private void checkCapacity() {
if (size >= capacity()) {
container = Arrays.copyOf(container, container.length * 2);
}
}
//添加一个元素
public void add(Object obj) {
checkCapacity();
container[size++] = obj;
}
//校验index的合理性范围
private void checkIndex(int index) {
if (index < 0 || index >= size) {
throw new IndexOutOfBoundsException(index + "越界");
}
}
//获取[index]位置的元素
public Object get(int index) {
checkIndex(index);
return container[index];
}
//替换[index]位置的元素
public void set(int index, Object value) {
checkIndex(index);
container[index] = value;
}
//在[index]位置插入一个元素value
public void insert(int index, Object value) {
//(1)考虑下标的合理性:校验index的合理性范围
checkIndex(index);
//(2)总长度是否够:检查是否需要扩容
checkCapacity();
//(3)[index]以及后面的元素往后移动,把[index]位置腾出来
/*
* 假设total = 5, container.length= 10, index= 1
* 有效元素的下标[0,4]
* 移动:[1]->[2],[2]->[3],[3]->[4],[4]->[5]
* 移动元素的个数:size-index
*/
System.arraycopy(container, index, container, index + 1, size - index);
//(4)放入新元素
container[index] = value;
//(5)有效元素的个数增加
size++;
}
//返回所有实际存储的元素
public Object[] getAll() {
return Arrays.copyOf(container, size);
}
//删除[index]位置的元素
public void remove(int index) {
/*
* (1)校验index的合理性范围
* (2)移动元素,把[index+1]以及后面的元素往前移动
* (3)把container[size-1]=null 让垃圾回收器尽快回收
* (4)总元素个数减少 size--
*/
//(1)考虑下标的合理性:校验index的合理性范围
checkIndex(index);
//(2)移动元素,把[index+1]以及后面的元素往前移动
/*
* 假设total=8, container.length=10, index = 3
* 有效元素的范围[0,7]
* 移动:[4]->[3],[5]->[4],[6]->[5],[7]->[6]
* 移动了4个:size-index-1
*/
System.arraycopy(container, index + 1, container, index, size - index - 1);
//(3)把container[size-1]=null 让垃圾回收器尽快回收
container[size - 1] = null;
//(4)总元素个数减少 size--
size--;
}
//查询某个元素的下标
public int indexOf(Object obj) {
if (obj == null) {
for (int i = 0; i < size; i++) {
if (container[i] == null) {
return i;
}
}
} else {
for (int i = 0; i < container.length; i++) {
if (obj.equals(container[i])) {
return i;
}
}
}
return -1;
}
//删除数组中的某个元素(若有重复,只删第一个)
public void remove(Object obj) {
//查询索引
int index = indexOf(obj);
//按索引删除
if (index != -1) {
remove(index);
}
}
}
3、Iterator
iterator(迭代器)部分源码如下:
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
}
4、Iterable、Collection
iterable:可迭代的
collection:聚集
import java.util.Iterator;
public interface Collection<E> extends Iterable<E> {
// Query Operations
int size();
boolean isEmpty();
boolean contains(Object o);
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
// Modification Operations
boolean add(E e);
boolean remove(Object o);
// Bulk Operations
boolean containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
boolean removeAll(Collection<?> c);
boolean retainAll(Collection<?> c);
void clear();
}
4.1、List(类似Python的list)
- List是接口,元素有顺序,元素可重复
- 其下实现类有
ArrayList
(遍历效率更高)和LinkedList
(增删效率更高) - 部分源码如下
import java.util.Iterator;
public interface List<E> extends Collection<E> {
int size(); // 获取元素个数
boolean isEmpty(); // 是否为空
boolean contains(Object o); // 是否包含
Iterator<E> iterator();
Object[] toArray();
<T> T[] toArray(T[] a);
boolean add(E e); // 添加一个
boolean remove(Object o); // 移除一个
boolean containsAll(Collection<?> c); // 是否包含全部
boolean addAll(Collection<? extends E> c); // 添加多个
boolean addAll(int index, Collection<? extends E> c);
boolean removeAll(Collection<?> c); // 移除多个
boolean retainAll(Collection<?> c); // ≈交集
void clear(); // 清空所有元素
// Positional Access Operations
E get(int index); // 按索引取值
E set(int index, E element); // 按索引设置值
void add(int index, E element); // 按索引添加值
E remove(int index); // 按索引移除值(并返回)
// Search Operations
int indexOf(Object o); // 根据值找索引(第一个)
int lastIndexOf(Object o); // 根据值找索引(最后一个)
// View
List<E> subList(int fromIndex, int toIndex); // 子集(切片)
}
4.1.1、LinkedList
import java.util.LinkedList;
// 创建ArrayList对象
LinkedList<String> ls = new LinkedList<>();
// 添加元素
ls.addFirst("2");
ls.addLast("3");
ls.add(1, "4");
// 查看
System.out.println(ls); // [2, 4, 3]
// 子列表
System.out.println(ls.subList(1, 2)); // [4]
// 按索引获取
System.out.println(ls.get(0)); // 2
// 包含
System.out.println(ls.contains("4")); // true
4.2、Set
- Set特点:元素不可重复
HashSet
:底层是HashMap
,无序的TreeSet
:底层是TreeMap
,顺序按照元素的大小LinkedHashSet
:是HashSet
的子类,比父类多维护了元素的添加顺序
4.2.1、HashSet(有点像Python的set)
import java.util.HashSet;
- 元素不能重复,元素没有顺序
- 部分源码如下,它使用了
HashMap
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable
{
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
public HashSet(int initialCapacity, float loadFactor) {
map = new HashMap<>(initialCapacity, loadFactor);
}
public Iterator<E> iterator() {
return map.keySet().iterator();
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean contains(Object o) {
return map.containsKey(o);
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
public boolean remove(Object o) {
return map.remove(o)==PRESENT;
}
public void clear() {
map.clear();
}
}
5、Map
Map<K,V>
put
:设置键值对get
:按键获取值entrySet
:遍历键值对
5.1、HashMap(类似Python的dict)
import java.util.HashMap;
// 创建
HashMap<String, Integer> map = new HashMap<>();
// 添加
map.put("剑圣", 8);
map.put("先知", 6);
map.put("巫妖", 5);
System.out.println(map); // {巫妖=5, 先知=6, 剑圣=8}
// 获取
System.out.println(map.get("剑圣")); // 8
System.out.println(map.get("大法师")); // null
// 移除
System.out.println(map.remove("先知")); // 6
System.out.println(map); // {巫妖=5, 剑圣=8}
// 获取全部键或值
System.out.println(map.keySet()); // [巫妖, 剑圣]
System.out.println(map.values()); // [5, 8]
// 遍历键值对
for (Map.Entry<String, Integer> stringIntegerEntry : map.entrySet()) {
System.out.println(stringIntegerEntry);
} // 巫妖=5 剑圣=8
6、Collections
import java.util.ArrayList;
import java.util.Collections;
public class Hello {
public static void main(String[] args) {
ArrayList<Integer> a = new ArrayList<>();
// 一次性给a添加多个元素
Collections.addAll(a, 5, 4, 3, 2, 1);
System.out.println(a);
// 乱序
Collections.shuffle(a);
System.out.println(a);
// 排序(默认升序)
Collections.sort(a);
System.out.println(a);
// 二分查找
int i = Collections.binarySearch(a, 4);
System.out.println(i);
}
}