Java编程思想:第11章 持有对象

Java容器框架提供了多种不同特性的容器方便我们管理任意数量的对象。

11.1泛型和类型安全的容器

JavaSE5之前的容器允许我们向其中放入不同类型的对象,但是取出的时候需要进行类型强制转换,很容易出现问题。有了泛型之后需要我们只能向集合里添加指定类型及其子类,取出时也不需要类型转换,这个功能是编译器完成的。

11.2基本概念

Java中容器分为2个概念:

1)Collection:一个独立元素的序列,这些元素都服从一条或多条规则。List必须按照插入顺序保存元素,Set不能有重复元素。Queue按照排队规则确定对象产生的顺序(通常与插入顺序相同)。

2)Map:映射表允许我们使用另一个对象来查找某个对象,它也被称为关联数组。

理想情况下在创建对象时向上转型成接口,方便切换实现,除非需要用到特有方法。

11.3添加一组元素

java.util包里Arrays和Collections类有很多实用方法,可以在一个Collection里添加一组元素。

Arrays.asList()接受一个数组或者可变长参数,转换成一个固定长度(不能add)的list。

Collections.addAll()接受一个Collection对象和一个可变长参数,向Collection里添加一组元素。

Collection.addAll()只能接收一个Collection对象,没有上面灵活。

首选:Collections.addAll()

注意Arrays.asList()对产生的List类型做了最理想的假设:即最近公共父类,这可能会产生问题。

比如:父类Person,一级子类Man,二级子类WhiteMan、BlackMan

List<Person> list = Arrays.asList(new WhiteMan(),new BlackMan());

这时会编译出错,因为编译器找到理想的类型是List<Man>而不是List<Person>,我们需要手动指定asList类型:

List<Person> list = Arrays.<Person>asList(new WhiteMan(),new BlackMan());

使用Collections.addAll()不需要指定类型,因为第一个参数里已经有类型了。

11.4容器的打印

容器类不需要做特殊处理即可直接打印,并且可以生成格式很好的输出。

11.5List

List可以把元素维护在特定的序列里,在Collection基础上添加了很多操作index的方法,如获取,设置,插入,删除。contains()/remove()/indexOf()等行为是根据equals()结果来的。

ArrayList提供高效访问,低效插入删除。LinkedList相反,一般情况使用ArrayList除非进行大量插入删除使用ArrayList影响性能了才改用LinkedList。

subList()可以返回大List里的一个子List,相当于对象引用copy了一份,所以list.containsAll(subList)是true,对subList元素的操作也会影响原来的list。

retainAll()交集操作,保留两个list里的共同元素,基于equals()。

其他方法...

11.6迭代器

任何容器都必须有某种方式可以插入元素并把它取回。对于List可以用get取出,如果从更高层次思考,会发现要使用容器,必须对容器的确切类型编程。如果我们可以使得对List使用的代码也可以对Set使用,就会非常方便。迭代器Iterator就是用于这种目的,使得我们不需要关心序列的底层结构就可以使用next()、hasNext()、remove()来遍历或删除序列中的元素。如果只是遍历不需要修改List,使用foreach更加方便。

remove()用了删除最后一次next()得到的元素,所以必须先使用next()取得元素,然后才能remove()

11.6.1ListIterator

是一个更加强大的Iterator子类型,但只能用于List类型的访问。允许进行双向(前后)访问,可以使用set替换被访问的最后一个元素,可以用listIterator(n)重载方法创建一个一开始就指向索引为n的ListIterator.

11.7LinkedList

LinkedList相比ArrayList除了某些操作性能不同之外,还添加了可以使其用作栈,队列或双端队列的方法。很多方法名称不同但是作用差不多,主要用于在不同上下文环境下使用。如element() getFirst(),remove(),removeFirst(),peek(),pop()等。

11.8Stack

后进先出,就像弹夹一样的储存装置。LinkedList有实现栈功能的所有方法,可以直接作为栈使用,但是有时候用一个真正的栈更可以把问题解决清楚优雅。

Java中提供了java.util.Stack类用于模拟一个Stack,但是设计的并不恰当:

1)Stack从Vector继承而来,多了很多不必要的方法,而且有的方法会破坏Stack的规则:如add(index,obj)

2)Vector是数组实现的,在push()pop()时候效率很低,应该使用链式结构

我们可以自己写一个:

public class Stack<T>{

  private LinkedList<T> list = new LinkedList<>();

  public void push(T t){list.addFirst(t);}

  public T peek(){return list.getFirst();}

  public T pop(){return list.removeFirst();}

  public boolean empty(){return list.isEmpty();}

  public String toString(){return list.toString();}

}

11.9Set

Set不保存重复的元素,最常用的功能就是测试是否包含对象,因此查找就成为了Set中最重要的操作,HashSet实现专门对快速查找进行了优化。

Set与Collection接口完全一致,只是行为不同(多态的特性)。Set是根据对象的值来决定归属性的。

11.10Map

把对象映射到其它对象的能力是解决很多问题的杀手锏。

遍历时可以返回键的set或者键值对的set

11.11Queue

队列是典型的先进先出FIFO容器,取出顺序与放入顺序一致。是一种可靠的把对象从程序的某个区域传输到另一个区域的途径。在并发编程中特别重要。

LinkedList提供了方法可以支持队列行为,并且也实现了Queue接口,所以它可以用作Queue的一种实现。

Queue<Integer> q = new LinkedList<>();

offer():插入队尾或返回false

peek()/element()返回队头,队列为空时peek返回null,element抛出NoSuchElementException

poll()/remove()返回队头并移除,队列为空时pool返回null,remove抛出NoSuchElementException

11.11.1PriorityQueue

队列规则:给定一组队列元素,确定下一个弹出元素的规则。先进先出是典型的一种队列规则。

优先级队列声明下一个弹出元素是优先级最高的元素。PriorityQueue在JavaSE5开始出现,默认元素顺序是按照自然顺序弹出,可以设置一个Comparator来控制这种优先级。

11.12Collection和Iterator

实现Collection接口就必须提供创建Iterator的方法,保证了通用能力。

11.13ForEach与迭代器

任何实现了Iterable接口的类,都可以在foreach中使用。数组和Collection类都可以使用。但Map不行。

11.13.1适配器方法惯用法

使用不同的方法产生不同的Iterator,用于不同的迭代方式。

11.14总结

Java提供了大量持有对象的方式:

1.数组,保存单一类型对象,可以是多维的,可以保存基本类型,但是容量不可变。

2.Collection保存单一元素,Map保存键值对。有了泛型不需要进行类型转换,不可持有基本类型但是会自动包装来处理,容量可变。

3.List也建立了数字和对象的关联,所以数组和List都是排序好的容器。List能自动扩容

4.大量随机访问用ArrayList,经常中间插入删除用LinkedList。

5.队列以及堆栈行为用LinkedList完成。

6.Map是一种将对象与对象相关联的设计。HashMap用于快速访问,TreeMap保持键始终处于排序状态,没有HashMap快。LinkedHashMap保持元素插入顺序,但是也通过散列提供了快速访问的能力。

7.Set不接受重复元素。HashSet提供最快查询速度,TreeSet保持元素处于排序状态,LinkedHashSet以插入顺序保存元素,查询也快。

8.不应该使用过时的Vector(同步效率慢,扩容时候翻倍ArrayList扩一半),HashTable(同步效率慢),Stack

集合框架图:

原文地址:https://www.cnblogs.com/superzhao/p/4820041.html