容器部分归纳为三篇来写,分别从基本用法,深入研究,以及在算法中的应用。本章主要介绍基本用法。
Java中有多种方式保存对象,比如简单的数组,它是编译器支持的类型。数组是保存一组对象的最有效的方式,如果想保存一组基本类型数据,这也是常推荐的方式。但数组具有固定的尺寸,通常在写程序中不知道有多少对象要保存,或者有更复杂的方式来保存时,数组尺寸固定不足就显现出来,下面主要介绍java类库提供的一套完整的容器类,容器提供了许多完善的方法来保存和操作对象,并且可以自动调整自己的尺寸大小。
上图是简单的容器分类关系图,从图中可以看出集合类主要分两大接口类,Collection和Map。还有两个工具类Collections和Arrays。下面分别介绍Collection和Map的实现子接口的常用方法及实现。
collection接口概括了序列的概念——一种存放一组对象的方式。任何继承自Collection的类的对象都可以正常使用其所有方法工作。Collection的常用方法以及利用工具类。
public class CollectionFuctions {
public static void main(String[] args) {
Collection collection = new ArrayList(Arrays.asList(1,2,3,4,5));
Integer[] moreIntegers = {6,7,8,9,0};
collection.addAll(Arrays.asList(moreIntegers));
Collections.addAll(collection, 11,12,13);
Collections.addAll(collection, moreIntegers);
List list = Arrays.asList(14,15,16);//此方法的输出底层表示的是数组,不能调整尺寸大小所以delete()或add()不可用
list.set(1, 20);
System.out.println(collection);
System.out.println(list);
}
}
public class CollectionPrint {
static Collection fill(Collection collection){
collection.add("cat");
collection.add("fish");
collection.add("dog");
return collection;
}
static Map fill(Map map){
map.put("cat", "haha");
map.put("fish", "hehe");
map.put("dog", "xixi");
return map;
}
public static void main(String[] args) {
System.out.println(fill(new ArrayList()));
System.out.println(fill(new HashSet()));
System.out.println(fill(new HashMap()));
System.out.println(fill(new TreeMap()));
}
}
List可以将元素维护在特定的序列中。List接口在Collection的基础上添加了大量的方法使得可以在List的中间插入和移除元素,或者自我调整尺寸。
有两种类型的List:
LinkedList也像ArrayList一样实现了List接口,但它执行插入删除是比ArrayList更高效,但在随机访问时更逊色。LinkedList还添加了可以使其用作栈,队列或双端队列的方法。在LinkedList的基础上添加element(), offer(), peek(), poll(), remove()方法使其可以成为一个Queue的实现。但两者中很多方法的意义不同,具体请参考jdk。
迭代器(也是一种设计模式)是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员不必关心该序列底层的结构。此外,迭代器通常被称为轻量级对象:创建它的代价小。java的Iterator只能单向移动,通常
class Pet{
private String name;
public Pet(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Pet [name=" + name + "]";
}
}
public class IteratorTest {
public static void main(String[] args) {
List pets =new ArrayList(Arrays.asList(new Pet("nick"),new Pet("lily"),new Pet("tom")));
Iterator iterator = pets.iterator();
while(iterator.hasNext()){
Pet pet = iterator.next();
System.out.println(pet.getName());
}
iterator = pets.iterator();
for(int i=0;i<2;i++){
iterator.next();
iterator.remove();
}
System.out.println(pets);
}
}
Foreach语法主要用于数组,但它也可以用于任何Collection对象。在java中大量的类都是Iterator类型,主要包括所有的Collection类(但是不包括各种Map)。Foreach语法可以用于数组或其他任何Iterator但不意味着数组也是Iterator,而且任何自动包装也不会发生。
ListIterator是一个更加强大的Iterator的子类,它只能用于各种List类的访问,尽管Iterator只能向前移动但是ListIterator可以双向移动,它还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引,并且可以使用set()方法替换它访问过的最后一个元素。
栈通常是“后进先出”(LIFO)的容器。LinkedList具有能够实现栈的所有功能的方法,因此可以直接将LinkedList作为栈使用。
public class MockStack<T> {
private LinkedList storage = new LinkedList();
public void push( T v){storage.addFirst(v);}
public T peek(){return storage.getFirst();}
public T pop(){return storage.removeFirst();}
public boolean empty(){return storage.isEmpty();}
public String toString(){return storage.toString();}
}
Set跟Collection具有完全一样的接口没有任何额外的添加功能,不像前两个不同的List,实际上Set就是Collection,只是行为不同,Set是基于对象的值来确定归属性的。Set不保存重复的元素。Set最常用在被使用的是测试归属性,你可以很容易的询问某个对象是否在某个Set中。因此查找是Set中最重要的操作,因此我们通常会选择一个HashSet的实现,他专门对快速查找进行了优化。
队列是一个典型的先进先出(FIFO)容器,只可从一端插入从另一端删除,通常被当做一种可靠的将对象从程序的某个区域传输到另一个区域的途径。队列在并发编程中特别重要。LinkedList提供了方法以支持队列的行为,并且它实现了Queue接口,因此LinkedList可以用作Queue的一种实现。通过LinkedList向上转型为Queue。
先进先出描述了最典型的队列规则,是在指定的一组队列中元素的情况下,确定下一个弹出队列的元素的规则,下一个元素应该是等待时间最长的元素。优先级队列声明下一个弹出的元素是最需要的元素(具有最高优先级)。当你在PriorityQueue上调用offer()方法来插入一个对象时,这个对象会在队列中被排序。默认的排序将使用对象在队列中的自然顺序,但我们也可以提供自己的Comparator来修改顺序,PriorityQueue可以确保当你调用peek(), poll(), remove() 方法时获取元素是队列中优先级最高的。
数组与其他种类的容器之间区别有三方面:效率,类型,保存基本类型的能力。数组是一种效率最高的存储和随机访问对象引用序列的方式。数组就是一个简单的线性序列,这使得元素访问非常快。但是为这种速度所付出代价是数组对象的大小固定,并且在其生命周期不可变。虽然首选是ArrayList而不是数组,但ArrayList的效率比数组低很多。数组可以持有基本类型,而泛型之前的容器则不能,但有了泛型容器就可以指定并检查所持有对象的类型。
Comparator 集合外排序 基本类型无法使用
Comparable 集合内使用
Map与Collection接口一样可以容易的扩展到多维,而我们只需要将其值设置为Map,但我们可以很容易的将容器组合起来生成更强大的数据结构,如Map
public static void main(String[] args) {
Map map = new HashMap();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过Map.keySet遍历key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种
System.out.println("通过Map.entrySet使用iterator遍历key和value:");
Iterator> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过Map.entrySet遍历key和value");
for (Map.Entry entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}