书中的原标题是——holding your object,把握你的对象,译者翻译成持有对象。这是用的最多的类之一。
作者说的,如果一个程序包含固定数量的且其生命周期都是已知的对象,那么这是一个非常简单的程序。确实,如果数组大小已知,那么就很简单了。
除了数组,Java提供了容器类来holding object。
1)泛型和类型安全的容器
ArrayList,可以自动扩充大小的数组,add插入对象,get访问对象,size查看对象数目。
[java] view plain copy print?
- 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是什么,类还是接口,一查就知道了。
[java] view plain copy print?
- public interface List<E>
- extends Collection<E>
- public class ArrayList<E>
- extends AbstractList<E>
- implements List<E>, RandomAccess, Cloneable, Serializable
这样的关系就出来了。
3)添加一组元素
[java] view plain copy print?
- 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常用方法:
[java] view plain copy print?
- 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)迭代器
在没用迭代器之前,遍历是这样写的:
[java] view plain copy print?
- for(int i = 0; i < newList.size(); i++){
- System.out.println(newList.get(i));
- }
而迭代器被称为轻量级对象,创建的代价小。
[java] view plain copy print?
- Iterator<Member> iterator = members.iterator();
- while(iterator.hasNext()){
- System.out.println(iterator.next());
- }
next移动下一个元素,但是拿到的当前元素。hasNext检查是否还有元素,Iteratorf容器返回一个Iterator。
7)ListIterator
这个之前没听过,Iterator只能向前移动,ListIterator可以双向移动。
[java] view plain copy print?
- ListIterator<Member> iterator = members.listIterator();
- while(iterator.hasNext()){
- System.out.println(iterator.next());
- }
- while(iterator.hasPrevious()){
- System.out.println(iterator.previous());
- }
向前输出,向后输出。
8)LinkedList
插入移除高效。
方法很容易理解,属于自解释型的方法名。
[java] view plain copy print?
- 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,但是行为不同,这就是多态和继承的应用了。
[java] view plain copy print?
- 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,没有重复元素,顺序也无规律,其实是使用了散列,以后会提到。
如果:
[java] view plain copy print?
- Set<Integer> set = new TreeSet<Integer>();
会发现是有序的,TreeSet将元素存储在了红黑树里面。
书中有一处错误:LinkedHashSet写成LinkedHashList了,它也使用散列,但是看起来使用了链表维护元素插入顺序。
11)Map
作者说将对象映射到其他对象的能力是解决编程问题的杀手锏。
确实,例如查看随机数的分布,如果真是随机数的话,那么10000次产生20以内的随机数,每个数字出现的次数应该是相近的。
[java] view plain copy print?
- 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也用到多维。
[java] view plain copy print?
- Map<Person,List<TV>>;
12)Queue
先进先出,买基金的时候,由于后期追加购买,但是前期的基金已经满1个月,这样不用赎回费,然后问了客服之后发现,先买进的先赎回,然后自己大拍大腿,这不就是所谓的队列设计吗?
LinkedList实现了Queue接口。
对了,经常用到Random,竟然忘了说为什么作者传参用了47,其实Random传参是传入一个计算的种子,默认是系统时间,47在他看来一直是“魔幻数字”。
[java] view plain copy print?
- 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
直接看例子:
[java] view plain copy print?
- 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的时候优先级高的会现出来。
[java] view plain copy print?
- 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遍历:
[java] view plain copy print?
- 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语句。
[java] view plain copy print?
- 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。
[java] view plain copy print?
- 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的类似。