持有对象
java中引用太多了,所以使用容器装载。
泛型:
容器中装载不同对象,取出时候强制类型转换容易出错
class Apple {
private static long counter;
private final long id = counter++;
public long id() { return id;}
}
class Orange {}
public class ApplesAndOrangesWithoutGenerics {
public static void main(String[] args) {
ArrayList apples = new ArrayList();
for(int i = 0; i < 3; i++) {
apples.add(new Apple());
apples.add(new Orange());
}
for(int i = 0; i < apples.size(); i++){
((Apple)apples.get(i)).id();
}
}
}
应用预编译得到泛型非常简单,可以在编译器防止将错误类型的对象防止到容器中。
public class ApplesAndOrangesWithGenerics {
public static void main(String[] args) {
ArrayList<Apple> apples = new ArrayList<Apple>();
for(int i = 0; i < 3; i++){
apples.add(new Apple());
// apples.add(new Orange()); 编译报错
}
for(Apple apple : apples){
System.out.println(apple.id());
}
}
}
当你指定了某个类型作为泛型参数时,你并不仅限于只能将该确切类型的对象放置到容器中。向上转型也可以像作用于其他类型一样作用于泛型。
基本概念
java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念:
Collection。一个独立元素的序列。List按照插入的顺序保存元素、Set不可重复,无序、Queue队列
Map 一组成对的“键值对”对象,允许你使用键来查找值。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一些对相关联在了一起;或者被称为“字典”,因为你可以使用键对象来查找值对象,就像在字典中使用单词来定义一样。
Colction.add()
Collection.addAll()
Collections.addAll()
Arrays.asList();
如果使用Arrays.asList()的输出,将其当做List,但是在这种情况下,其底层表示的是数组,因此不能调整尺寸。如果尝试使用add()或者delete()在这种列表中添加或者删除元素,就可能引发去改变数组尺寸的尝试
class Snow {}
class Powder extends Snow {}
class Light extends Powder {}
class Heavy extends Powder {}
class Crusty extends Snow {}
class Slush extends Snow {}
public class AsListInference {
public static void main(String[] args) {
List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());
// List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());
List<Snow> snows3 = new ArrayList<Snow>();
Collections.addAll(snows3, new Light(), new Heavy());
List<Snow> snows4 = Arrays.<Snow>asList(new Light(), new Heavy());
}
}
可以像操作snows4的操作中所看到的,在Arrays.asList()中间插入一条"线索",以告诉编译器对于Arrays.asList()产生的List类型,实际的目标类型应该是什么。
List:
List承诺可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法,使得可以在List的中间插入和移除元素。
ArrayList:
LinkedList
constains()
remove()
indexOf()
equals()
优化是一个很棘手的问题,最好的策略就是置之不顾,指导你发现需要担心它了。
subList()允许你从较大的列表中创建出一个片段,而将其结果传递给这个较大的列表的containsAll()方法时,自然会得到true。
Collections.sort()
Collections.shuffle()
retainAll()有效的交集操作,依赖于equals方法。
removeAll()
set(),在指定的索引处,用第二个参数替换整个位置的元素
isEmpty()
clear()
迭代器
对于List,add()是插入元素的方法之一,get()是取出元素的方法之一
使用容器,必须对容器的确切类型编程。考虑在下面的情况:如果原本是对着List编码的,但是后来发现如果能够把相同的代码应用于Set,将会显得非常方便,此时怎么做?
迭代器的概念可以用于达成此目的,迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必知道或关心序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。
iterator()容器返回一个Iterator
next()获得序列中的下一个元素
hasNext()间插序列中是否还有元素
remove()移除由next()产生的最后一个元素
ListIterator
只能用于各类List的访问,ListIterator可以双向移动
LinkedList添加了可以作用于栈、队列或双端队列的方法
Stack可以内部包含一个LinkedList来完成模拟,util包里面的Vector是集成Vector实现的。
Set不保存重复的元素。Set无需,这是因为HashSet使用散列,TreeSet将元素存储在红-黑树数据结构中,HashSet使用的是散列函数,LinkedHashList因为查询速度的原因也使用了散列,但是看起来它使用链表来维护元素的插入顺序。
排序使用TreeSet
contains可以判断数据是否处于容器内
Queue
先进先出的容器
offer()将一个元素插入到队尾
peek()和element()在不移除的情况下返回队头,peek()在队列为空时返回null,element()抛出NoSuchElementException异常
pool和remove()移除并返回队头。为空的情况下前者返回null,后者抛异常
PriorityQueue
典型的队列规则是下一个元素应该是等待时间最长的元素。这个是优先级队列
优先级队列声明下一个弹出元素是最需要的元素(具有最高的优先级)。
PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然排序,但是你可以通过提供自己的Comparator来修改这个排序。
public class PriorityQueueDemo {
public static void main(String[] args) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<Integer>();
Random rand = new Random(47);
for(int i = 0; i < 10; i++){
priorityQueue.offer(rand.nextInt(i + 10));
}
printQueue(priorityQueue);
List<Integer> ints = Arrays.asList(25, 22, 20, 18, 14, 9, 3, 1, 1, 2, 3, 9, 14, 18, 21, 23, 25);
priorityQueue = new PriorityQueue<Integer>(ints);
printQueue(priorityQueue);
priorityQueue = new PriorityQueue<Integer>(ints.size(), Collections.<Integer>reverseOrder());
priorityQueue.addAll(ints);
printQueue(priorityQueue);
String fact = "EDUCATION SHOULD ESCHEW OBFUSCATION";
List<String> strings = Arrays.asList(fact.split(" "));
PriorityQueue<String> stringsPQ = new PriorityQueue<String>(strings);
printQueue(stringsPQ);
stringsPQ = new PriorityQueue<String>(strings.size(), Collections.<String>reverseOrder());
stringsPQ.addAll(strings);
printQueue(stringsPQ);
}
public static <T> void printQueue(Queue<T> queue){
for(T obj : queue){
System.out.print(obj + " ");
}
System.out.println();
}
}
Foreach与迭代器
目前为止,foreach语法主要用于数组,但是它也可以应用于任何Collection对象。
public class ForeachCollections {
public static void main(String[] args) {
Collection<String> cs = new LinkedList<String>();
Collections.addAll(cs, "Take the long way home".split(" "));
for(String s : cs){
System.out.print("‘" + s + "‘ ");
}
}
}
Foreach之所以能工作,是因为Java SE5引入了新的被称为Iterable的接口,该接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。因此如果你创建了任何实现Iterator的类,都可以将它用于forEach语句中。
public class IterableClass implements Iterable<String>{
protected String[] words = "And that is how we know the Earth to be banana-shaped.".split(" ");
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < words.length;
}
@Override
public String next() {
return words[index++];
}
public void remove() {
throw new UnsupportedOperationException();
}
};
}
public void forEach(Consumer<? super String> action) {
}
public Spliterator<String> spliterator() {
return null;
}
public static void main(String[] args) {
ArrayList al = new ArrayList();
for(String s : new IterableClass()){
System.out.println(s + " ");
}
}
}
iterator()方法返回的是实现了Iterator<String>的匿名内部类的实例,该匿名内部类可以遍历数组中的所有单词。在main中可以发现IterableClass确实可以用于foreach语句中。
foreach语句可以用于数组或者其他任何Iterable,但是这并不意味着数组肯定也是一个Iterable,而任何自动包装也不会自动发生
public class ArrayIsNotIterable {
static <T> void test(Iterable<T> ib) {
for(T t : ib){
System.out.println(t + " ");
}
}
public static void main(String[] args) {
test(Arrays.asList(1, 2, 3));
String[] strings = {"A", "B", "C"};
// test(strings); 编译报错
test(Arrays.asList(strings));
}
}
适配器方法惯用法
如果现在又一个Iterable类,你想要添加一种或多种在foreach语句中使用这个类的方法,应该怎么做呢?例如,假设你希望可以选择以向前的方向或是向后的方向迭代一个单词列表。如果直接继承这个类,并覆盖iterator()方法,你只能替换现有的方法,而不能实现选择。
一种解决方案是所谓适配器方法的惯用法。“适配器”部分设计模式,因为你必须提供特定接口以满足foreach语句。当你有一个接口并需要另一个接口时,编写适配器就可以解决问题。
class ReversibleArrayList<T> extends ArrayList<T> {
public ReversibleArrayList(Collection<T> c) { super(c); }
public Iterable<T> reversed() {
return new Iterable<T>() {
@Override
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() {
throw new UnsupportedOperationException();
}
};
}
};
}
}
public class AdapterMethodIdiom {
public static void main(String[] args) {
ReversibleArrayList<String> ral = new ReversibleArrayList<>(Arrays.asList("To be or not to be".split(" ")));
for(String s : ral){
System.out.print(s + " ");
}
System.out.println();
for (String s : ral.reversed()) {
System.out.print(s + " ");
}
}
}