集合
Java中集合继承关系:
- List , Set, Map都是接口,前两个继承至Collection接口,Map为独立接口
- Set下有HashSet,LinkedHashSet,TreeSet
- List下有ArrayList,Vector,LinkedList
- Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
- Collection接口下还有个Queue接口,有PriorityQueue类
1.Set
TreeSet:基于红黑树实现,支持有序性操作,例如根据一个范围查找元素的操作。但是查找效率不如 HashSet,HashSet 查找的时间复杂度为 O(1),TreeSet 则为 O(logN)。
HashSet:基于哈希表实现,支持快速查找,但不支持有序性操作。并且失去了元素的插入顺序信息,也就是说使用 Iterator 遍历 HashSet 得到的结果是不确定的。
LinkedHashSet:具有 HashSet 的查找效率,并且内部使用双向链表维护元素的插入顺序。
2.List
ArrayList:基于动态数组实现,支持随机访问。
Vector:和 ArrayList 类似,但它是线程安全的。
LinkedList:基于双向链表实现,只能顺序访问,但是可以快速地在链表中间插入和删除元素。不仅如此,LinkedList 还可以用作栈、队列和双向队列。
3.Queue
LinkedList:可以用它来实现双向队列。
PriorityQueue:基于堆结构实现,可以用它来实现优先队列。
迭代器
Iterator作为一个接口存在,源码如下
package java.util;
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
iterator实现源码:
private class Itr implements Iterator<E> {
int cursor = 0;
int lastRet = -1;
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
其中的重点是定义了expectedModCount,提供了一种Fast-Fail机制,用来保证迭代过程中集合的安全性。当集合进行修改时(包括改变集合结构、改变集合大小、打乱集合中的顺序等,不包括设置元素的值),modCount会增加,迭代器会将expectedModCount与modCount比对来看是否正确。如下操作就会报java.util.ConcurrentModifcationException:
List<String> list = new LinkedList<String>();
list.add("first");
list.add("second");
for(Iterator<String> iter = list.iterator();iter.hasNext();) {
String str = (String)iter.next();
System.out.println(str);
if(str.equals("second")) {
list.add("three");
}
}
Iterator和ListIterator的区别:
Iterator:正向遍历、获取和移除元素
ListIterator:双向遍历、支持元素修改
泛型
泛型有三种使用方式:泛型类、泛型接口、泛型方法
泛型只在编译阶段有效,编译时正确检验类型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。
List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();
Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();
if(classStringArrayList.equals(classIntegerArrayList)){
Log.d("泛型测试","类型相同");
}
泛型类
泛型类型用于类的定义中,被称为泛型类。 基本写法:
class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
private 泛型标识 /*(成员变量类型)*/ var;
.....
}
}
example:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Test<T>{
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Generic(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
Test<Integer> testInteger = new Test<Integer>(12345);
Test<String> testString = new Test<String>("keyValue");
Log.d("泛型测试","key is" + testInteger.getKey());
//结果输出:泛型测试: key is 12345
Log.d("泛型测试","key is" + testString.getKey());
//结果输出:泛型测试: key is keyValue
注意:1、泛型的类型参数只能是类类型,不能是基本类型
2、不能对确切的泛型类型使用instanceof操作,如num instanceof Test,这种操作编译时会出错
泛型接口
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
当实现泛型接口的类,未传入泛型实参时:
/**
* 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
当实现泛型接口的类,传入泛型实参时:
/**
* 传入泛型实参时:
* 定义一个生产器实现这个接口,虽然我们只创建了一个泛型接口Generator<T>
* 但是我们可以为T传入无数个实参,形成无数种类型的Generator接口。
* 在实现类实现泛型接口时,如已将泛型类型传入实参类型,则所有使用泛型的地方都要替换成传入的实参类型
* 即:Generator<T>,public T next();中的的T都要替换成传入的String类型。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
泛型通配符
我们知道Integer是Number的子类,那在使用 Test作为形参的函数中,能不能够传入Test使用呢?比如:
public void showKeyValue(Test<Number> obj){
System.out.println("泛型测试:key value is " + obj.getKey());
}
Test<Integer> testInt = new Test<Integer>(12);
Test<Number> testNumber = new Test<Number>(34);
showKeyValue(testNumber);
showKeyValue(testInt);
//showKeyValue(testInt)这个会报错:cannot be applied to Test<java.lang.Number>
由此可以看出,同一种泛型可以对应多种版本,但不同版本的实例是不兼容的。这种问题不就违背了多态的设计理念了吗?因此,我们可以使用类型通配符来解决这种问题。
public void showKeyValue(Test<?> obj){
System.out.println("泛型测试:key value is " + obj.getKey());
}
类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参 。此处的?和Number、String、Integer一样都是一种实际的类型,可以把?看成所有类型的父类。是一种真实的类型。
可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
泛型方法
线程体系结构
·Executor:线程池顶级接口,只定义了一个执行无返回值任务的方法;
·ExecutorService:线程池次级接口,对Executor做了一些扩展,增加了关闭线程池、执行有返回值任务、批量执行任务的方法;
·ScheduledExecutorService:对ExecutorService做了一些扩展,增加了定时任务功能,分执行一次和重复执行多次两种;
·AbstractExecutorService:实现ExecutorService接口的抽象类,实现了部分方法,主要为执行有返回值任务和批量执行任务的方法;
·ThreadPoolExecutor:继承AbstractExecutorService类的普通线程池类,线程池的主要实现逻辑都在这里面,包括创建线程、处理任务、拒绝的策略等;
·ScheduledThreadPoolExecutor:定时任务线程池类,继承了ThreadPoolExecutor,实现了ScheduledExecutorService,