- 泛型
如果一个程序只包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序
容器是没有规定数量的对象集,可以用以解决在创建之前无法知道到底需要创建多少对象的问题
泛型是程序设计语言的一种特性,允许程序员在强类型程序设计语言中定义一些可变的部分,这些可变的部分在使用前必须做出指明。
- 类型安全的容器
使用Java泛型的一个问题是,编译器允许你插入不正确的类型,如ArrayList容器,通过add()方法插入一个Object对象,它不会去判
断具体的类型,可以插入new Apple()或者new Orange(),当我们想要调用Apple类型里的方法时,必须把ArrayList.get(i)强制类型
转化为Apple类型,此时如果在容器中插入了一个Orange对象,就会出错
想要定义Apple类型的ArrayList类型容器
不能使用 ArrayList apples = new ArrayList();
而应该使用 ArrayList<Apple>来指明存储的类型,这可以防止在编译器将错误的类型对象放入到容器中
当你指定了某个类型作为泛型参数时,并不仅限于把确切类型的对象放置到容器中,向上转型也可以作用于容器
比如
ArrayList<Apple> applies = new ArrayList<Apple>
class Fuji extends Apple {}
applies.add(new Fuji());
这里Apple的子类型Fuji也可以被保存在Apple类型的容器中
1.Collection
一个独立元素的序列,这些元素都符合一条或者多条规则
2.Map
一组成对的“键值对”,允许你用键来查找值
List<Apple> = new ArrayList<Apple>
可以创建一个具体类的对象,并把它转型为对应的接口
Arrays.asList(1,2,3,4,5)生成一个数组
collection.addAll({1,2,3,4,5})向collection中添加元素
Collections.addAll(collection, {1,2,3,4,5}) 向原有collection中添加元素
Arrays.asList没有注意到你对它赋予了什么类型,所以有时候需要现实地告诉它
Arrays.<type>asList 这被称为显式类型参数说明
容器类型
ArrayList ,LinkedList都是List类型,都按照被插入的顺序保存元素
HashSet,TressSet,LinkedHashSet都是Set类型,每个相同的项只保存一次,HashSet是最快的获取元素方式,TreeSet按照比较
结果的升序保存元素,LinkedHashSet按照被添加的顺序保存元素
Map使得你可以通过键来查找对象,键所关联的对象称为值,HashMap,TreeMap,LinkedHashMap都是Map,HashMap提供了
最快的查询技术,TreeMap按照比较结果的升序来保存键,LinkedHashMap则按照插入顺序保存键,同时保留了HashMap的查询速
度
- List
将元素维护在特定的序列中
可以插入和移除元素
- ArrayList
数组型List,长于随机访问元素,但是在List中间插入和移除元素较慢
- LinkedList
链表型List,通过代价较低的在List中间进行插入和删除操作,提供了优化的顺序访问,但在随机访问方面相对较慢
contains()方法,确定某个对象是否在List中
remove()方法,把某个对象从List中移除
indexOf()方法,查看某个对象在List中的索引值
这三个方法传入对象引用时,都用到了equals()方法来比较,所以每一个对象在List都是唯一的,即使是相同内容,相同类的两个对象,因为引用不同,也不是完全相同
当equal()的行为不同时,List中对象比较的方式也不相同,如String只有在内容完全一样的时候才会是等价的
subList(index1, index2)取出一个子序列
containsAll(list)是否包含子列表,这里比较的时候与子列表的顺序无关
retainAll(list)求一个交集,保留同时在两个列表中的元素
- Set
Set不保存重复元素
HashSet用散列函数保存,TreeMap用红黑树保存
LinkedHashMap也用散列函数保存
- Map
Map保存键值对
自动包装机制可以将int这样的基本类型转化成为HashMap可以使用的Integer引用
map.get(key) 获取值
map.put(key, value) 存入键值对
map.containsKey(key) 是否存在这个key
map.containsValue(value) 是否存在这个value
map.keySet() map的键列表
map.values() map的值列表
- Queue
队列是一个先进先出(FIFO)的序列
即从容器的一端放入事物,从另一端取出
Java中Stack和Queue都是通过LinkedList实现
Java中Stack和Queue中栈顶元素和队首元素都是序列中第一个元素,删除操作也都是删除第一个元素栈顶或队首
唯一的不同是,入栈操作是加入元素到第一个元素,成为新的栈顶,而入队操作是加入元素到最后一个元素
peek() 队首元素,即第一个元素
offer() 插入元素到队列中
poll()或remove() 移除并返回第一个元素
PriorityQueue
队列规则是指在给定一组队列中的元素的情况下,确定下一个弹出队列的元素的规则
优先队列声明下一个弹出元素是最需要的元素
当在PriorityQueue中调用offer()方法插入一个元素时,这个元素会在队列中被排序
- Collection与迭代器
Collection接口是描述所有序列容器的共性的根接口
java.util.AbstractCollection类提供了Collection的默认实现
可以针对接口,而非任何具体实现来编码,例如
public void display(Collection<T> tc) {
for(T t : tc)
print(t);
}
对于这个方法,任何实现了Collection的类都可以应用
在Java中,实现Collection就意味着要实现迭代器,这两者被绑定在了一起,一个Collection类必须包含iterator()方法
public void display(Iterator<T> it) {
while(it.hasNext()) {
T t = it.next();
print(t);
}
}
display(collection);
display(collection.iterator());
必须能同时被使用
- foreach与迭代器
foreach主要用于数组,但它也可以用于任何的Collection对象
Collection<X> collection = new LinkedList<X>();
for(X x : collection)
print(x);
之所以能够这样使用,是因为实现了一个Iterable接口,该接口包含了一个能够产生Iterator的iterator()方法,并且Iterable被foreach
用来在序列中移动
可见所有的Collection类都是Iterable类型的,它们都需要实现iterator()方法和size()方法
public class IterableClass implements Iterable<String> {
protected String[] words = "To be or not to be".split(" ");
public Iterator<String> iterator() {
return new Iterator() {
private int index = 0;
public boolean hasNext() { return index > words.length; }
public String next() { return words[index++]; }
public void remove() {}
};
}
}
- 适配器方法惯用法
public Iterable<T> reversed() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return new Iterator<T>() {
int current = size() - 1;
public boolean hasNext() { return current > -1; }
public T next() { return get(current--); }
public void remove() {}
};
}
};
}
这里的reversed()方法获取了一个新的Iterable实现类的对象,这个实现类里面有一个新的迭代器Iterator的实现,这个实现重写了
hasNext()方法和next()方法的行为,使它成为一个反向遍历的迭代器
List<Integer> list = new ArrayList<Integer>(Arrays.asList(array));
当对list打乱顺序时,发现array同时也被打乱了,可知Arrays.asList()产生的List对象会使用底层数组作为其物理实现