Java可以用数组和其他容器类来(List、Set、Queue、Map)来解决这个问题,不同的容器类有各自的特性,满足不同的需求。
Java SE5之前是没有范型的,一个容器内(以List为例)可以放置任意的对象。
public class Test {
// 用@SuppressWarnings抑制编译器对“不受检查的异常”的警告
@SuppressWarnings("unchecked")
public static void main(String[] args) {
// (1)
List strList = new ArrayList() {
{
add("spidersama");
add(520);
}
};
// (2)
for (Object obj : strList) {
System.out.println(((String) obj).length());
}
}
}
没有范型的问题:
如上所示,(1)处的代码在编译和运行的时候都没有任何问题;但是当你在(2)处需要把Object类型强制转型成你需要的对象类型的时候,这个时候就会出现问题,因为Integer是无法转型成String类型的。
范型的好处
Java容器类类库的用途是“保存对象”,并将其划分为两个不同的概念。
- Arrays.asList();
- Collections.addAll();
- collection.addAll();
- Collection的构造器可以接受一个Collection来初始化
Collection<Integer> collection = new ArrayList<>(Arrays.asList(1, 2, 3));
collection.addAll(Arrays.asList(new Integer[]{1, 2, 3}));
Arrays.asList(new int[]{1, 2, 3});
Arrays.asList(1, 2, 3);
ollections.addAll(collection, new Integer[]{1, 2, 3});
Collections.addAll(collection, 1, 2, 3);
注意:Arrays.asList()返回的ArrayList不可以添加元素,此ArrayList和java.util.ArrayList不是同一个类。
List是一种可修改的序列,它允许在创建之后添加、移除元素,或者自我调整尺寸。
有两种基本的List:
迭代器是一个对象,他的工作是遍历并选择序列中的对象,而程序员不必知道该序列的底层结构,即将遍历序列的操作和序列底层的结构分离。
迭代器通常被成为轻量级对象:创建它的代价很小。
Java的迭代器Iterator
更强大的迭代器 ListIterator
如前所述,LinkedList也像ArrayList一样实现了基本的List接口,但是它执行插入和移除时比ArrayList要更高效,但是在随机访问操作方面要慢。
LinkedList还添加了可以使其用作栈、队列或双端队列的方法(是Queue的子类)。
LinkedList中的相同方法
栈是一种先进后出(Last In First Out,LIFO)的容器(数据结构)。LinkedList能实现栈所有的功能。
主要方法
- peek():返回栈顶元素(如果没有栈顶元素,返回为null)
- pop():返回并移除栈顶元素(如果没有栈顶元素,返回为null)
- push():元素入栈
- empty():栈是否为空
Set不保存重复的元素。
Set与Collection有完全一样的接口(方法),因此没有任何额外的功能,实际上Set就是Collection只是行为不同(这就是继承和多态思想的典型应用:体现不同的行为)。
常用的几种Set
键值对
主要方法:
get()方法 获得相应key值对应的Vaule值
set()方法 设置相应key值对应的Vaule值
keySet()方法 产生所有键值组成的Set
values()方法 产生所有值组成的Set
队列是一个典型的先进先出(First In First Out,FIFO)的容器。队列通常被当作一种可靠的将对象从程序的一个区域传输到另一个区域的途径,尤其在并发编程中十分重要。
主要方法:
offer():将元素插入队尾。
add():同offer(),但是当超出队列长度当时候抛出异常。
peek():不移除的返回队头元素;为空时返回null。
element():同peek(),为空时抛出NoSuchElementException。
poll():移除并返回队头元素,为空时返回null。
remove():同poll(),为空时抛出NoSuchElementException异常。
优先级队列:按照优先级的顺序维护的队列。
当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中自然顺序,但是你可以通过提供自己的Comparator来修改这个顺序。PriorityQueue可以确保当你调用相关方法时,获取的元素将是队列中优先级最高的元素。
Java中,实现Collection就必须提供iterator()方法,这有的时候会带来麻烦。直接生成Iterator是将队列与消费队列的方法链接在一起耦合度最小的方式,并且与实现Collection相比,它在序列类上所施加的约束也少得多。
foreach可以用于数组和所有Collection对象,之所以能够工作,是因为Collection继承了Iterable接口。
数组不是Iterable。
Iterable接口包含一个能够产生Iterator的iterator()方法,并且Iterable接口被foreach用来在序列中移动。换言之,任何实现了Iterable接口的类,都可以用与foreach语法。
如果需要多个foreach遍历一个类的方法,例如该类需要支持向前和向后遍历。这是只实现Iterable是不行的,可以编写其他返回类型为Iterable的方法来满足foreach语句。这就是编写适配器。
// 反向遍历部分代码
public class Test<E> extends ArrayList<E> {
public Test(Collection<? extends E> c) {
super(c);
}
public Iterable<E> reverse() {
return new Iterable<E>() {
@Override
public Iterator<E> iterator() {
return new Iterator<E>() {
int index = size() - 1;
@Override
public boolean hasNext() {
return index >= 0;
}
@Override
public E next() {
return get(index--);
}
};
}
};
}
}
容器分类图解