Set集合、list集合

1. List概览

List,就像它的名字暗示的一样,是一组排列有序的元素。当我们讨论List的时候,很容易将它和Set作比较。Set是一组唯一的而且排列无序的元素。

下图是集合类的层次结构图。你可以总体上知道我们今天讨论的主题。

2. ArrayList vs. LinkedList vs. Vector

从上图可知,它们都实现了List接口。它们的用法差不多,主要的区别在于它们对于不同操作的操作速度不同。

ArrayList是可以改变大小的数组。当有元素添加到ArrayList中去时,它的大小动态的增加。元素可以直接通过get()和set()方法进行访问,因为ArrayList实际上是数组。LinkedList是个双向链表。它的add()和remove()方法比ArrayList快,但是get()和set()方法却比ArrayList慢。Vector和ArrayList类似,但是Vector是同步的。如果在线程安全的环境下,使用ArrayList是更好的选择。添加元素的时候,当超过初始容量的时候,Vector和ArrayList需要更多的空间:Vector需要将数组的大小增加一倍,而ArrayList需要增加50%。

LinkedList还实现了Queue接口,这样就比ArrayList和Vector多出了一些方法如offer(), peek(), poll()等。

注意:ArrayList的初始容量(initial capacity)很小。我们应该设置一个比较大的初始容量,这样可以避免重新改变大小。

3. ArrayList的例子

 
 
 
 
 
 
 
 
 
 
 
 
ArrayList al = new ArrayList();
al.add( 3 );
al.add( 2 );      
al.add( 1 );
al.add( 4 );
al.add( 5 );
al.add( 6 );
al.add( 6 );
  
Iterator iter1 = al.iterator();
while (iter1.hasNext()){
     System.out.println(iter1.next());
}

4. LinkedList的例子

 
 
 
 
 
 
 
 
 
 
LinkedList ll = new LinkedList();
ll.add( 3 );
ll.add( 2 );      
ll.add( 1 );
ll.add( 4 );
ll.add( 5 );
ll.add( 6 );
ll.add( 6 );
  
Iterator iter2 = ll.iterator();
while (iter2.hasNext()){
     System.out.println(iter2.next());
}

由上可见,它们的用法相同,主要的区别在于它们内部的实现,以及操作的复杂度的不同。

5. Vector

Vector几乎和ArrayList相等,主要的区别在于Vector是同步的。正因为此,Vector比ArrayList的开销更大。通常大部分程序员都使用ArrayList,他们可以自己写代码进行同步。

6. ArrayList vs. LinkedList的性能比较

我使用下面的代码测试它们的性能:

 
 
 
 
 
 
 
 
ArrayList arrayList = new ArrayList();
LinkedList linkedList = new LinkedList();
  
// ArrayList add
long startTime = System.nanoTime();
  
for ( int i = 0 ; i < 100000 ; i++) {
     arrayList.add(i);
}
long endTime = System.nanoTime();
long duration = endTime - startTime;
System.out.println( "ArrayList add:  " + duration);
  
// LinkedList add
startTime = System.nanoTime();
  
for ( int i = 0 ; i < 100000 ; i++) {
     linkedList.add(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println( "LinkedList add: " + duration);
  
// ArrayList get
startTime = System.nanoTime();
  
for ( int i = 0 ; i < 10000 ; i++) {
     arrayList.get(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println( "ArrayList get:  " + duration);
  
// LinkedList get
startTime = System.nanoTime();
  
for ( int i = 0 ; i < 10000 ; i++) {     linkedList.get(i); } endTime = System.nanoTime(); duration = endTime - startTime; System.out.println( "LinkedList get: " + duration); // ArrayList remove startTime = System.nanoTime(); for (int i = 9999; i >=0; i--) {
     arrayList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println( "ArrayList remove:  " + duration);
  
// LinkedList remove
startTime = System.nanoTime();
  
for ( int i = 9999 ; i >= 0 ; i--) {
     linkedList.remove(i);
}
endTime = System.nanoTime();
duration = endTime - startTime;
System.out.println( "LinkedList remove: " + duration);

输出如下:

 
ArrayList add:  13265642
LinkedList add: 9550057
ArrayList get:  1543352
LinkedList get: 85085551
ArrayList remove:  199961301
LinkedList remove: 85768810

它们的性能的差别很显著。LinkedList对于add()和remove()相对于ArrayList要快,但是get()要慢些。按照复杂度以及测试结果来看,我们很容易知道什么时候该使用ArrayList,什么时候该使用LinkedList。简而言之,下面的情况该使用LinkedList:

Set集合不包含重复的元素,这是使用Set的主要原因。有三种常见的Set实现——HashSet, TreeSet和LinkedHashSet。什么时候使用它们,使用哪个是个重要的问题。总体而言,如果你需要一个访问快速的Set,你应该使用HashSet;当你需要一个排序的Set,你应该使用TreeSet;当你需要记录下插入时的顺序时,你应该使用LinedHashSet。

1. Set接口

Set接口继承了Collection接口。Set集合中不能包含重复的元素,每个元素必须是唯一的。你只需将元素加入set中,重复的元素会自动移除。

2. HashSet vs. TreeSet vs. LinkedHashSet

HashSet是采用hash表来实现的。其中的元素没有按顺序排列,add()、remove()以及contains()等方法都是复杂度为O(1)的方法。

TreeSet是采用树结构实现(红黑树算法)。元素是按顺序进行排列,但是add()、remove()以及contains()等方法都是复杂度为O(log (n))的方法。它还提供了一些方法来处理排序的set,如first(), last(), headSet(), tailSet()等等。

LinkedHashSet介于HashSet和TreeSet之间。它也是一个hash表,但是同时维护了一个双链表来记录插入的顺序。基本方法的复杂度为O(1)。

3. TreeSet的例子

 
TreeSet tree = new TreeSet();
tree.add( 12 );
tree.add( 63 );
tree.add( 34 );
tree.add( 45 );
  
Iterator iterator = tree.iterator();
System.out.print( "Tree set data: " );
while (iterator.hasNext()) {
     System.out.print(iterator.next() + " " );
}

输出如下:

 
Tree set data: 12 34 45 63

现在让我们定义一个Dog类:

 
class Dog {
     int size;
  
     public Dog( int s) {
         size = s;
     }
  
     public String toString() {
         return size + "" ;
     }
}

我们将“dog”添加到TreeSet中:

 
import java.util.Iterator;
import java.util.TreeSet;
  
public class TestTreeSet {
     public static void main(String[] args) {
         TreeSet dset = new TreeSet();
         dset.add( new Dog( 2 ));
         dset.add( new Dog( 1 ));
         dset.add( new Dog( 3 ));
  
         Iterator iterator = dset.iterator();
  
         while (iterator.hasNext()) {
             System.out.print(iterator.next() + " " );
         }
     }
}

编译正常,但是运行时出错:

 
Exception in thread "main" java.lang.ClassCastException: collection.Dog cannot be cast to java.lang.Comparable
     at java.util.TreeMap.put(Unknown Source)
     at java.util.TreeSet.add(Unknown Source)
     at collection.TestTreeSet.main(TestTreeSet.java:22)

因为TreeSet是有序的,Dog类必须实现java.lang.Comparable的compareTo()方法才行:

 
class Dog implements Comparable{
     int size;
  
     public Dog( int s) {
         size = s;
     }
  
     public String toString() {
         return size + "" ;
     }
  
     @Override
     public int compareTo(Dog o) {
             return size - o.size;
     }
}

输出:

 
1 2 3

4. HashSet的例子

 
HashSet dset = new HashSet();
dset.add( new Dog( 2 ));
dset.add( new Dog( 1 ));
dset.add( new Dog( 3 ));
dset.add( new Dog( 5 ));
dset.add( new Dog( 4 ));
Iterator iterator = dset.iterator();
while (iterator.hasNext()) {
     System.out.print(iterator.next() + " " );
}

输出:

 
5 3 2 1 4

注意输出顺序是不确定的。

5. LinkedHashSet的例子

 
LinkedHashSet dset = new LinkedHashSet();
dset.add( new Dog( 2 ));
dset.add( new Dog( 1 ));
dset.add( new Dog( 3 ));
dset.add( new Dog( 5 ));
dset.add( new Dog( 4 ));
Iterator iterator = dset.iterator();
while (iterator.hasNext()) {
     System.out.print(iterator.next() + " " );
}

输出的顺序时确定的,就是插入的顺序。

 
2 1 3 5 4

6. 性能测试

下面的代码测试了以上三个类的add()方法的性能。

 
public static void main(String[] args) {
   
     Random r = new Random();
   
     HashSet<Dog> hashSet = new HashSet<Dog>();
     TreeSet<Dog> treeSet = new TreeSet<Dog>();
     LinkedHashSet<Dog> linkedSet = new LinkedHashSet<Dog>();
   
     // start time
     long startTime = System.nanoTime();
   
     for ( int i = 0 ; i < 1000 ; i++) {
         int x = r.nextInt( 1000 - 10 ) + 10 ;
         hashSet.add( new Dog(x));
     }
     // end time
     long endTime = System.nanoTime();
     long duration = endTime - startTime;
     System.out.println( "HashSet: " + duration);
   
     // start time
     startTime = System.nanoTime();
     for ( int i = 0 ; i < 1000 ; i++) {
         int x = r.nextInt( 1000 - 10 ) + 10 ;
         treeSet.add( new Dog(x));
     }
     // end time
     endTime = System.nanoTime();
     duration = endTime - startTime;
     System.out.println( "TreeSet: " + duration);
   
     // start time
     startTime = System.nanoTime();
     for ( int i = 0 ; i < 1000 ; i++) {
         int x = r.nextInt( 1000 - 10 ) + 10 ;
         linkedSet.add( new Dog(x));
     }
     // end time
     endTime = System.nanoTime();
     duration = endTime - startTime;
     System.out.println( "LinkedHashSet: " + duration);
   
}

从输出看来,HashSet是最快的:

 
HashSet: 2244768
TreeSet: 3549314
LinkedHashSet: 2263320
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值