集合类

----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流!    ----------------------

Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
Map
├Hashtable
├HashMap
└WeakHashMap

Collection接口
  
Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Elements)。一些 Collection允许相同的元素而另一些不行。一些能排序而另一些不行。Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。
  所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个 Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后 一个构造函数允许用户复制一个Collection。
  如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持一个iterator()的方法,该方法返回一个迭代子,使用该迭代子即可逐一访问Collection中每一个元素。典型的用法如下:
    Iterator it = collection.iterator(); // 获得一个迭代子
    while(it.hasNext()) {
      Object obj = it.next(); // 得到下一个元素
    }
  由Collection接口派生的两个接口是List和Set。

List接口
  List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。
和下面要提到的Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个 ListIterator接口,和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加,删除,设定元素, 还能向前或向后遍历。
  实现List接口的常用类有LinkedList,ArrayList,Vector和Stack。

LinkedList类
  LinkedList实现了List接口,允许null元素。此外LinkedList提供额外的get,remove,insert方法在 LinkedList的首部或尾部。这些操作使LinkedList可被用作堆栈(stack),队列(queue)或双向队列(deque)。
  注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(...));

ArrayList类
  ArrayList实现了可变大小的数组。它允许所有元素,包括null。ArrayList没有同步。
size,isEmpty,get,set方法运行时间为常数。但是add方法开销为分摊的常数,添加n个元素需要O(n)的时间。其他的方法运行时间为线性。
  每个ArrayList实例都有一个容量(Capacity),即用于存储元素的数组的大小。这个容量可随着不断添加新元素而自动增加,但是增长算法 并没有定义。当需要插入大量元素时,在插入前可以调用ensureCapacity方法来增加ArrayList的容量以提高插入效率。
  和LinkedList一样,ArrayList也是非同步的(unsynchronized)。

Vector类
  Vector非常类似ArrayList,但是Vector是同步的。由Vector创建的Iterator,虽然和 ArrayList创建的Iterator是同一接口,但是,因为Vector是同步的,当一个Iterator被创建而且正在被使用,另一个线程改变了 Vector的状态(例如,添加或删除了一些元素),这时调用Iterator的方法时将抛出 ConcurrentModificationException,因此必须捕获该异常。

Stack 类
  Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

Set接口
  Set是一种不包含重复的元素的Collection,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。
  很明显,Set的构造函数有一个约束条件,传入的Collection参数不能包含重复的元素。
  请注意:必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

Map接口
  请注意,Map没有继承Collection接口,Map提供key到value的映射。一个Map中不能包含相同的key,每个key只能映射一个 value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。

Hashtable类
  Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
  添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
使用Hashtable的简单示例如下,将1,2,3放到Hashtable中,他们的key分别是”one”,”two”,”three”:
    Hashtable numbers = new Hashtable();
    numbers.put(“one”, new Integer(1));
    numbers.put(“two”, new Integer(2));
    numbers.put(“three”, new Integer(3));
  要取出一个数,比如2,用相应的key:
    Integer n = (Integer)numbers.get(“two”);
    System.out.println(“two = ” + n);
  由于作为key的对象将通过计算其散列函数来确定与之对应的value的位置,因此任何作为key的对象都必须实现hashCode和equals方 法。hashCode和equals方法继承自根类Object,如果你用自定义的类当作key的话,要相当小心,按照散列函数的定义,如果两个对象相 同,即obj1.equals(obj2)=true,则它们的hashCode必须相同,但如果两个对象不同,则它们的hashCode不一定不同,如 果两个不同对象的hashCode相同,这种现象称为冲突,冲突会导致操作哈希表的时间开销增大,所以尽量定义好的hashCode()方法,能加快哈希 表的操作。
  如果相同的对象有不同的hashCode,对哈希表的操作会出现意想不到的结果(期待的get方法返回null),要避免这种问题,只需要牢记一条:要同时复写equals方法和hashCode方法,而不要只写其中一个。
  Hashtable是同步的。

HashMap类
  HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key。,但是将HashMap视为Collection时(values()方法可返回Collection),其迭代子操作时间开销和HashMap 的容量成比例。因此,如果迭代操作的性能相当重要的话,不要将HashMap的初始化容量设得过高,或者load factor过低。

WeakHashMap类
  WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。

 

Colletion类的操作,大抵相同,增删、排序、去重;根据性能、是否同步选取合适的类。我们拿其中的TreeSet详细说一下

 

TreeSet小例

TreeSet是依靠TreeMap来实现的。
TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。
我们可以在构造TreeSet对象时,传递实现Comparator接口的比较器对象。

下面是一个简单的例子

public class TreeSetTest 
{
	
	public static void main(String[] args)
	{
		
		TreeSet set=new TreeSet();
		
		set.add("66666");
		set.add("454545");
		set.add("dfdf");

		
		Iterator it=set.iterator();
		
		while(it.hasNext())
		{
			String line=(String)it.next();
			System.out.println(line);
		}
		
			
	
				
		
	}
}


 

TreeSet与Comparable接口

TreeSet作为一个集合,可以添加自定义对象,但是一旦添加自定义对象,因为对象的不可比较性,我们需要实现Comparable接口。

class Person implements Comparable{
	private String name;
	private int age;
	
	Person(String name,int age)
	{
		this.name=name;
		this.age=age;
	}
	
	public String getName()
	{
		return name;
	}
	
	public int getAge()
	{
		return age;
	}
	
	public int compareTo(Object o)
	{
		if(!(o instanceof Person))
			throw new RuntimeException("此对象不是Perso2对象");
		
		Person p=(Person)o;
		
		if(this.age>p.age)
			return 1;
		else if(this.age==p.age)
			return 0;
		return -1;
		

	}
}

上面的代码实现了对age升序排序。

那么TreeSet又是如何实现对象的排序呢,TreeSet底层实际上一个二叉树,也就是红黑树. 因此TreeSet就被定义称“Tree"Set。了解了解红黑树的概念:

红黑树是一种自平衡排序二叉树,树中每个节点的值,都大于或等于在它的左子树中的所有节点的值,并且小于或等于在它的右子树中的所有节点的值,这确保红黑树运行时可以快速地在树中查找和定位的所需节点。

对于 TreeMap 而言,由于它底层采用一棵“红黑树”来保存集合中的 Entry,这意味这 TreeMap 添加元素、取出元素的性能都比 HashMap 低:当 TreeMap 添加元素时,需要通过循环找到新增 Entry 的插入位置,因此比较耗性能;当从 TreeMap 中取出元素时,需要通过循环才能找到合适的 Entry,也比较耗性能。但 TreeMap、TreeSet 比 HashMap、HashSet 的优势在于:TreeMap 中的所有 Entry 总是按 key 根据指定排序规则保持有序状态,TreeSet 中所有元素总是根据指定排序规则保持有序状态。

为了理解 TreeMap 的底层实现,必须先介绍排序二叉树和红黑树这两种数据结构。其中红黑树又是一种特殊的排序二叉树。

排序二叉树是一种特殊结构的二叉树,可以非常方便地对树中所有节点进行排序和检索。

排序二叉树要么是一棵空二叉树,要么是具有下列性质的二叉树:

  • 若它的左子树不空,则左子树上所有节点的值均小于它的根节点的值;
  • 若它的右子树不空,则右子树上所有节点的值均大于它的根节点的值;
  • 它的左、右子树也分别为排序二叉树。

图 1 显示了一棵排序二叉树:


图 1. 排序二叉树


对排序二叉树,若按中序遍历就可以得到由小到大的有序序列。如图 1 所示二叉树,中序遍历得:

 

创建排序二叉树的步骤,也就是不断地向排序二叉树添加节点的过程,向排序二叉树添加节点的步骤如下:

  1. 以根节点当前节点开始搜索。
  2. 拿新节点的值和当前节点的值比较。
  3. 如果新节点的值更大,则以当前节点的右子节点作为新的当前节点;如果新节点的值更小,则以当前节点的左子节点作为新的当前节点。
  4. 重复 2、3 两个步骤,直到搜索到合适的叶子节点为止。
  5. 将新节点添加为第 4 步找到的叶子节点的子节点;如果新节点更大,则添加为右子节点;否则添加为左子节点。

 TreeSet与Comparator接口

可要是实际项目中,我们 compareTo的方式需要改变,该引用对象又不是自己的,我们怎么办。

这个时候我们就可以采取第二中排序方式:

TreeSet排序的第一种方式;让元素就自身具备比较性。元素需要实现 Comparable接口,覆盖compareTo方法,也就是我们上面所说的方式。

TreeSet排序的第二中方式;当元素自身不具备比较性的时候,或者具备的侥幸不是需要的,就需要让自神具备比较性。

也是实现采用TreeSet(Comparator<? superE> comparator)构造方法,自己编写一类,实现comparator接口,覆盖compare方法。

class MyCompare implements Comparator
{
	public int compare(Object o1,Object o2)
	{
		Person2 p1=(Person2)o1;
		Person2 p2=(Person2)o2;
		
		int num=p1.getName().compareTo(p2.getName());
		if(num==0)
		{
			return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
		}
		
		return num;
			
		
		
		
	}
}


  TreeSet的泛型

我们看看下面这个例子:

public class TreeSetTest 
{
	
	public static void main(String[] args)
	{
		
		TreeSet set=new TreeSet();
		
		set.add("str1");
		set.add("str2");
		set.add("str3");
		set.add(1);

		
		Iterator it=set.iterator();
		
		while(it.hasNext())
		{
			String line=(String)it.next();  //编译通过,运行报错
			System.out.println(line);
		}		
	}
}

TreeSet作为一个集合类型,是可以加入任意对象的,但是在实际运用中,一般加入的都只是一类对象,一旦加入其它对象,在获取的时候,就可能报错。

在JDK1.5之前,TreeSet的对象类型有赖于程序员的主管判断,这样是不安全的。

自JDK1.5之后,引入了新特性--泛型,能够客观主动的解决这一问题。

public class TreeSetTest 
{
	
	public static void main(String[] args)
	{
		
		TreeSet<String> set=new TreeSet<String>();
		
		set.add("str1");
		set.add("str2");
		set.add("str3");
		set.add(new Integer(1));    //显示的提示 ,不允许加入其它对象

		
		Iterator<String> it=set.iterator();  //迭代器也是泛型的
		
		while(it.hasNext())
		{
			String line=it.next();  //不再需要转换
			System.out.println(line);
		}		
	}
}



 ---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流!    ----------------------


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值