
书中的原标题是——holding your object,把握你的对象,译者翻译成持有对象。这是用的最多的类之一。
作者说的,如果一个程序包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。确实,如果数组大小已知,那么就很简单了。
除了数组,Java提供了容器类来holding object。
1)泛型和类型安全的容器
ArrayList,可以自动扩充大小的数组,add插入对象,get访问对象,size查看对象数目。
-
class Apple{}
-
-
public class Box {
-
public static void main(String[] args) {
-
ArrayList<Apple> a = new ArrayList<Apple>();
-
a.add(new Apple());
-
}
-
}
泛型(就是跟在ArrayList后面的那个尖括号指明Apple类型的标识)的添加可以在编译期间防止将错误类型的对象放进容器中。
同样,容器也可以用foreach语法。
2)概念
容器类作用是保存对象。分为两个:
一、Collection,List顺序保存元素,Set不能有重复元素,Queue按照排队来。
二、Map,键值对,通过键找值或者被称为字典。
对了,写了这么多篇,忘记说一件重要的事情了,懂得查API文档,最好看英文版。Collection是什么,类还是接口,一查就知道了。
-
public interface List<E>
-
extends Collection<E>
-
-
-
public class ArrayList<E>
-
extends AbstractList<E>
-
implements List<E>, RandomAccess, Cloneable, Serializable
这样的关系就出来了。
3)添加一组元素
-
public class AddGroup {
-
public static void main(String[] args) {
-
Collection<Integer> c = new ArrayList<Integer>(Arrays.asList(1,2,3,4));
-
Integer[] group = {5,6,7,8 };
-
c.addAll(Arrays.asList(group));
-
System.out.println(c);
-
Collections.addAll(c, 9,0);
-
System.out.println(c);
-
}
-
}
-
-
//[1, 2, 3, 4, 5, 6, 7, 8]
-
//[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
Collections.addAll,Adds all of the specified elements to the specified collection.
将其他元素添加到Collection c中,而Collection.addAll是添加一组元素。
4)容器的打印
其实上面的代码已经看出,具体的容器已经实现了自己的toString方法。
5)List
List有两种:ArrayList,随机访问元素快,中间插入和删除操作慢。
LinkedList,随机访问慢,但是中间插入和删除快,类似链表。
List常用方法:
-
class Member{
-
int age;
-
Member(int i){
-
age = i;
-
}
-
public String toString(){
-
return "member"+age;
-
}
-
}
-
-
public class ListMethod {
-
public static void main(String[] args) {
-
List<Member> members = new ArrayList<Member>();
-
Member member1 = new Member(1);
-
-
//添加元素
-
members.add(member1);
-
-
//判断容器是否为空
-
System.out.println(members.isEmpty());
-
-
//判断容器是否包含该元素
-
System.out.println(members.contains(member1));
-
-
//显示索引
-
System.out.println(members.indexOf(member1));
-
-
//移除元素
-
members.remove(member1);
-
System.out.println(members);
-
-
Member member2 = new Member(2);
-
Member member3 = new Member(3);
-
Member member4 = new Member(4);
-
members.add(member2);
-
members.add(member3);
-
members.add(member4);
-
-
//类似subString,从索引0开始截取到1,包含0和1
-
System.out.println(members.subList(0, 2));
-
-
//移除 不同于remove
-
//removeAll(Collection<?> c)
-
//remove(int index) remove(Object o)
-
members.removeAll(members);
-
System.out.println(members);
-
}
-
-
}
6)迭代器
在没用迭代器之前,遍历是这样写的:
-
for(int i = 0; i < newList.size(); i++){
-
System.out.println(newList.get(i));
-
}
而迭代器被称为轻量级对象,创建的代价小。
-
Iterator<Member> iterator = members.iterator();
-
while(iterator.hasNext()){
-
System.out.println(iterator.next());
-
}
next移动下一个元素,但是拿到的当前元素。hasNext检查是否还有元素,Iteratorf容器返回一个Iterator。
7)ListIterator
这个之前没听过,Iterator只能向前移动,ListIterator可以双向移动。
-
ListIterator<Member> iterator = members.listIterator();
-
while(iterator.hasNext()){
-
System.out.println(iterator.next());
-
}
-
-
while(iterator.hasPrevious()){
-
System.out.println(iterator.previous());
-
}
向前输出,向后输出。
8)LinkedList
插入移除高效。
方法很容易理解,属于自解释型的方法名。
-
public class TestLinkedList {
-
public static void main(String[] args) {
-
LinkedList<Member> members = new LinkedList<Member>();
-
Member member1 = new Member(1);
-
Member member2 = new Member(2);
-
Member member3 = new Member(3);
-
members.add(member1);
-
members.add(member2);
-
members.add(member3);
-
-
//返回列表头
-
System.out.println(members.peek());
-
-
//移除并返回列表头
-
System.out.println(members.removeFirst());
-
System.out.println(members);
-
-
//返回并移除表头
-
System.out.println(members.poll());
-
System.out.println(members);
-
-
//removelast 移除最后一个
-
members.add(member1);
-
members.add(member2);
-
System.out.println(members.removeLast());
-
System.out.println(members);
-
-
-
//addLast和add一样 都是往列表尾插入元素 addFirst自然就是表头
-
members.add(member2);
-
members.addFirst(member2);
-
members.addLast(member2);
-
System.out.println(members);
-
}
-
}
10)Set
Set,元素不重复。
Set与Collection有完全一样的接口,但是不同List,虽然Set就是Collection,但是行为不同,这就是多态和继承的应用了。
-
public class TestSet {
-
public static void main(String[] args) {
-
Set<Integer> set = new HashSet<Integer>();
-
Random r = new Random(400);
-
-
for(int i = 0;i<20;i++){
-
set.add(r.nextInt(300));
-
}
-
System.out.println(set);
-
}
-
}
HashSet,没有重复元素,顺序也无规律,其实是使用了散列,以后会提到。
如果:
-
Set<Integer> set = new TreeSet<Integer>();
会发现是有序的,TreeSet将元素存储在了红黑树里面。
书中有一处错误:LinkedHashSet写成LinkedHashList了,它也使用散列,但是看起来使用了链表维护元素插入顺序。
11)Map
作者说将对象映射到其他对象的能力是解决编程问题的杀手锏。
确实,例如查看随机数的分布,如果真是随机数的话,那么10000次产生20以内的随机数,每个数字出现的次数应该是相近的。
-
public class TestMap {
-
public static void main(String[] args) {
-
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
-
Random r = new Random(47);
-
-
//map里面为空
-
System.out.println(map.get(1));
-
for (int i = 0; i < 10000; i++) {
-
int j = r.nextInt(10);
-
Integer temp = map.get(j);
-
map.put( j ,temp == null ? 1 : temp+1);
-
}
-
System.out.println(map);
-
}
-
}
-
-
//result:null
-
//{0=994, 1=1033, 2=1010, 3=1014, 4=958, 5=1000, 6=1052, 7=980, 8=946, 9=1013}
数字确实是随机分布的,Map也很好用。
Map也用到多维。
12)Queue
先进先出,买基金的时候,由于后期追加购买,但是前期的基金已经满1个月,这样不用赎回费,然后问了客服之后发现,先买进的先赎回,然后自己大拍大腿,这不就是所谓的队列设计吗?
LinkedList实现了Queue接口。
对了,经常用到Random,竟然忘了说为什么作者传参用了47,其实Random传参是传入一个计算的种子,默认是系统时间,47在他看来一直是“魔幻数字”。
-
public class TestQueue {
-
public static void main(String[] args) {
-
Queue<Integer> q = new LinkedList<Integer>();
-
Random r = new Random(47);
-
for (int i = 0; i < 10; i++) {
-
//将一个元素插入队尾
-
q.offer(r.nextInt(12));
-
}
-
//返回队头
-
System.out.println(q.peek());
-
System.out.println(q);
-
}
-
}
一、PriorityQueue
直接看例子:
-
public class TestPriorityQueue {
-
public static void main(String[] args) {
-
String s = "What 's your favorite number ,1 or 2?";
-
List<String> l = Arrays.asList(s.split(""));
-
PriorityQueue<String> pq = new PriorityQueue<String>(l);
-
-
while(pq.peek()!=null){
-
System.out.print(pq.remove()+" ");
-
}
-
}
-
}
-
-
result:
-
[, , , , , b, , 1, , i, e, n, h, ', , a, o, 2, ?, y, t, t, o, u, u, m, r, e,
-
r, f, ,, s, a, v, r, o, W, r]
-
-
' , 1 2 ? W a a b e e f h i m n o o o r r r r s t t u u v y
先级最高最先弹出,在优先队列里面 最小的值拥有最高的优先级,如果为String,从上面看,空格优先级最高。
直接输出并不会排序,这和我之前预想的不一样。
要顺序输出,需要用peek方法,返回队头,空返回null,然后remove的时候优先级高的会现出来。
-
public static void main(String[] args) {
-
String s = "werwerwer";
-
List<String> l = Arrays.asList(s.split(""));
-
PriorityQueue<String> pq = new PriorityQueue<String>(l);
-
while (pq.peek() != null) {
-
System.out.print(pq.remove() + " ");
-
}
-
pq = new PriorityQueue<String>(l);
-
System.out.println(pq.peek());
-
pq.remove();
-
System.out.println(pq.peek());
-
pq.remove();
-
System.out.println(pq.peek());
-
}
-
-
result:
-
e e e r r r w w w
-
e
-
e
结果让我奇怪的是String里面有空格。这个看了字符串再回来解决。
13)Foreach与迭代器
foreach遍历:
-
public class Box {
-
public static void main(String[] args) {
-
Collection<String> c = new LinkedList<String>();
-
String s = "you are so great";
-
Collections.addAll(c,s.split(" "));
-
for(String string : c){
-
System.out.println(string);
-
}
-
}
-
}
原来foreach是Java SE5引入的特性,因为同时也引入了Iterable接口,接口产生Iterator的Iterator方法,Iterable被Foreach用来在序列中移动。所以实现Iterable的接口的类都可以用于Foreach语句。
-
All Known Subinterfaces:
-
BeanContext, BeanContextServices, BlockingDeque<E>, BlockingQueue<E>,
-
Collection<E>, Deque<E>, DirectoryStream<T>, List<E>, NavigableSet<E>,
-
Queue<E>, Set<E>, SortedSet<E>
其实Iterable的子接口有这么多,接下来实现Collection接口的自然也就实现了Iterable接口,所以也适用。
总结:
先看容器分类图:

点线为接口,实线为具体类,空心箭头指实现接口,实心箭头指某个类可以生成箭头所指向的类的对象。
1、数组可以存放对象,存放基本类型的数据,可以多维,就是容量不能改变。
2、Collection保存单一的元素,Map保存键值对。
3、List和数组类似,但是List可以自动扩充容量。大量随机访问使用ArrayList,经常进行插入删除操作,用LinkedList。
-
public class Box {
-
public static void main(String[] args) {
-
long start = System.currentTimeMillis();
-
Random r = new Random(47);
-
LinkedList<Integer> l = new LinkedList<Integer>();
-
for(int i = 0; i< 1000000; i++){
-
l.add(r.nextInt(1000));
-
}
-
long end = System.currentTimeMillis();
-
System.out.println(end - start);
-
-
ArrayList<Integer> a = new ArrayList<Integer>();
-
for(int i = 0; i< 1000000; i++){
-
a.add(r.nextInt(1000));
-
}
-
long end2 = System.currentTimeMillis();
-
System.out.println(end2 -end);
-
}
-
}
4、Map,HashMap用来快速访问,TreeMap保持键的排序,速度没HashMap快,LinkedHashMap保持元素排序,也通过散列也能快速访问,于两者中间。
5、Set元素不重复,TreeSet,HashSet,LinkedHashSet与Map的类似。