持有对象——Java容器

数组是保存一组基本数据类型的最优先选择的数据结构,但是在某些情况下需要保存一系列的自定义对象,Java提供了一套相当完整的容器类来结局这个问题(List、Set、Queue、Map)。这些容器均有自己的特色,如Set可以保证保存的对象互异,Map保存k-v对(关联数组)。容器类可以自动调整自己的容量,因此在编程时,不用担心容器的容量问题(前提是你有足够大的内存)。

  一、泛型

容器类在默认情况下是接收Object类的,换种说法,就是接收任何类型的类。泛型的作用就是在创建一个容器对象时,指定该容器对象接收的对象类型。例如:

package test;

import java.util.ArrayList;
/*
 * ArrayList不使用泛型,add()接收任何非法的类型。
 * orange可以添加到apples中,编译时无错误,运行时出现“类型转换错误”。
 * */
public class AppleOrangeWithoutGeneric {
	public static void main(String[] args){
		ArrayList apples = new ArrayList();
		Apple apple = new Apple();
		Orange orange = new Orange();
		apples.add(apple);
		apples.add(orange);
		for (int i = 0; i<apples.size(); i++){
			((Apple)apples.get(i)).eat();
		}
	}
}

class Apple{
	public void eat(){}
}

class Orange{
	public void eat(){
		
	}
}

  在编译时,以上示例代码并无问题,但是会出现运行时错误,因为强制类型转换是非法的。

比较安全一些的做法是在定义apples时指定该ArrayList只能接受Apple的对象。例如:

package test;

import java.util.ArrayList;

public class AppleOrangeWithGeneric {
	public static void main(String[] args){
		ArrayList<Apple> apples = new ArrayList<Apple>();
		Apple apple = new Apple();
		Orange orange = new Orange();
		apples.add(apple);
          //Compile error //apples.add(orange); for (int i = 0; i<apples.size(); i++){ ((Apple)apples.get(i)).eat(); } } } class Apple{ public void eat(){} } class Orange{ public void eat(){ } }

  二、分类

  1. Collection 保存独立对象
    1. List:必须保持插入的顺序保存数组
      1. LinkedList
      2. ArrayList
    2. Stack
    3. Set:不能有重复的元组
      1. HashSet
      2. TreeSet
    4. Queue
  2. Map:保存一组相关对象
    1. HashMap
    2. TreeMap

    List

    List有两种基本的类型,LinkedList在随机访问方面先对比较慢,ArrayList适用于随机访问,但是中间插入和移除元素时比较慢。

    基本操作:add() get() equals() remove() retainAll() subList() containsAll() toArray()

    迭代器可以提高代码复用程度。迭代器是一个队形,他的工作原理是遍历并选择序列中的对象,使用迭代器可以不用关心List的类型;

      1) 使用iterator()方法返回一个Iterator,准备返回序列中的第一个元素。

      2)使用next()方法获得当前位置的下一个元素。

      3)使用hasNext()方法判断是否到达序列尾部。

      4)使用remove()方法将当前元素删除。

    迭代器统一了对容器的访问方式,使得遍历序列的操作与程序底层的结构分离。

    

    ArrayList
    LinkedList

    使用方法较ArrayList更丰富,移除和插入效率更高。LinkedList添加了可以使其用作栈、队列或双端队列的方法。

    Stack

    先进后出,一种使用了LinkedList的实现:

package test;

import java.util.LinkedList;

public class StackTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Stack<String> stack = new Stack<String>();
		stack.push("str1");
		stack.push("str2");
		
		System.out.println(stack.pop());
		System.out.println(stack.pop());
	}

}

class Stack<T>{
	private LinkedList<T> storage = new LinkedList<T>();
	public void push(T t){storage.addFirst(t);}
	public T pop() {return storage.removeFirst();}
	public boolean isEmpty() {return storage.isEmpty();}
	public String toString() {return storage.toString();}
}

  Set

  Set不会保存重复的元素。Set最常被使用的场景是判断归属性,使用Set可以轻易地查询某个对象是否存在某个Set中。HashSet专门对快速查找进行了优化。Set具有与Collection完全一样的接口,除此之外无其他功能。

package test;

import java.util.*;
public class SetOfInteger {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Random rand = new Random(1);
		Set<Integer> set = new HashSet<Integer>();
		for(int i = 0; i<1000; i++){
			set.add(rand.nextInt(30));
		}
		System.out.println(set.toString());
	}

}

  输出

[15, 8, 23, 16, 7, 22, 9, 21, 6, 1, 29, 14, 24, 4, 19, 26, 11, 18, 3, 12, 27, 17, 2, 13, 28, 20, 25, 10, 5, 0]

  即使在代码中修改rand的seed值,输出仍然不发生变化,但输出的顺序无规律可言。这是因为set在内部对所存储的值进行了散列。TreeSet将元素维护在一颗红黑树中,因此想要获得有序的元素的话,最好使用TreeSet或者LinkedHaskSet:

 

package test;

import java.util.*;
public class TreeSetOfInteger {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Random rand = new Random(1);
		Set<Integer> set = new TreeSet<Integer>();
		for(int i = 0; i<1000; i++){
			set.add(rand.nextInt(30));
		}
		System.out.println(set.toString());
	}

}

输出:

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

 

   Queue

  队列,典型的先进先出容器。LinkedList实现了Queue的接口,可以向上转型为Queue。offer()方法将一个元素添加到队尾。peek()和element()方法都在不移除的情况下,返回队头,但是peek()在队为空时,返回null,后者在同样情况下返回nosuchelementexception。

  Map

  存储k-v对。如下面例子,key为某个数字,value是使用random产生的该数字的次数。

package test;
import java.util.*;
public class Statistics {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Random rand = new Random(100);
		Map<Integer,Integer> map = new HashMap<Integer,Integer>();
		for(int i = 0; i<1000;i++){
			int tmp = rand.nextInt(10);
			Integer count = map.get(tmp);
			map.put(tmp, count==null ? 1 : ++count);
		}
		System.out.println(map.toString());

	}

}

  输出:

{2=96, 4=92, 9=102, 8=108, 6=99, 1=95, 3=105, 7=95, 5=99, 0=109}

  Map中的元素也可以是Map,类似于多维数组。

总结

  • Collection保存单一的元素,Map保存k-v对。
  • 有了泛型,可以指定容器存放数据的类型,避免强制类型转换。
  • Collection和Map都可以自动调整内存。
  • List(Collection的导出类)有序,类似于数组,可用数字脚标访问元素。
  • 大量随机访问使用ArrayList,大量增/删操作使用LinkedList。
  • Queue和Stack的行为时有LinkedList提供。
  • HashMap访问速度快,TreeMap使用红黑树保证键的顺序。LinkedHashMap提供了快速访问并保留了有序。
  • Set中元素互异,HashSet提供最快的查询速度,而TreeSet保持元素处于排序状态。LinkedHashSet以插入顺序保存元素。
  • 不建议使用Vector、HashTable、Stack

  

posted on 2015-03-10 21:56  甲马  阅读(276)  评论(0编辑  收藏  举报

导航