Java中的集合框架-Collection(一)

一,Collection接口

  在日常的开发工作中,我们经常使用数组,但是数组是有很多的局限性的,比如:数组大小固定后不可修改,只能存储基本类型的值等等。

  基于数组的这些局限性,Java框架就产生了用于解决此类问题的工具,即集合框架。

  Java中有许多的集合框架类,基于这些类的共性特征,向上高度抽取,便形成了共性的集合框架接口-Collection。

  由于此接口属于工具性质的,所以它属于util包,java核心的内容在lang核心包里存放着。

  由于Collection是抽取了各集合的共性所形成的接口,所以所有的集合类都实现了此接口,此接口的特点如下:

    1,它是用于存储对象的容器

    2,可动态扩展容量

    3,不能存储基本类型的值

  我们在开发中对数组或集合的操作无非是增删改查四种操作,当然Collection接口既然是对各种集合的高度抽取的操作,它里面的方法也无非这四种。下面对这四种方法进行分类总结

    1,添加

      add  将指定的某一个元素添加到集合中

      addAll  将指定的集合对象一次性添加到集合中

    2,修改

      对集合的修改无非是获取集合中的某一个元素然后再重新赋给新值

    3,查询(获取)

      iterator  此方法返回一个迭代器对象,方便用于对集合中的每个元素进行循环获取操作

      contains  是否包含某个元素

      containsAll  是否包含另一个集合中的所有元素

      equals  比较两个集合是否相等

      isEmpty  判断一个集合是否为空

      size  获取集合中元素的数量

      retainAll  求和另外一个集合的交集

    4,删除

      remove  删除集合中的某一个元素

      removeAll  删除集合中某一个子集(参数为集合对象)

      clear  清除集合中所有的元素

    5,其它

      toArray  返回包含此集合元素的数组对象

      hashcode  返回此集合元素的哈希码

  以上是Collection接口的所有的方法,由于具体操作都由它的实现类来完成,并且都比较简单,所以演示代码在介绍他们的实现类的时候再列出。

  Collection接口有着许多的实现类及子接口,这些实现类或子接口中,一些是允许重复的,另外一些不允许重复;一些是有序的,而另外一些则是无序的。日常开发中比较常用的大致分为两类:List与Set(其实用了是这两个接口的实现类),下面用类图的方式从顶层往底层一层一层的添加Collection接口的子接口或实现类来把集合框架说完

                                

二,List接口

  1,List接口是有序的,这个有序是指存储的到集合中的元素的顺序与取出的顺序是一致的;

1     private static void function_demo2() {
2         List list = new ArrayList();
3         list.add(0);
4         list.add(2);
5         list.add(3);
6         for (int i = 0; i < list.size(); i++) {
7             System.out.println(list.get(i));
8         }
9     }

   这段程序的运行结果:

            与添加元素的顺序是一致的

 

  2,List接口里的元素允许重复;同样是上面的程序,我们再添加一个元素

          list.add(3);

    程序的运行结果如下:

              

 

  

  3,List接口里的元素允许有null值;还是上面的程序,我们添加一个null元素

          list.add(null);

    程序运行结果如下:

            

  4,允许像数组一样用索引进行元素位置的操作;索引的位置从0开始,和数组相类似

  5,List常用方法演示

 1     private static void function_demo1() {
 2         List list = new ArrayList();
 3         list.add("张三");// 将元素添加到列表的尾部
 4         list.add(0, "李四");// 将元素添加到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("赵六");
 8         list.addAll(list2);// 将一个集合中的元素添加到另一个集合中尾部
 9         list.addAll(0, list2);// 将一个集合中的元素添加到另一个集合中指定位置
10         for (int i = 0; i < list.size(); i++) {
11             System.out.println(list.get(i));// get方法获取指定位置的元素
12         }
13         list.contains("张三");// 集合是否存储了某一个元素,若存储了该元素则返回true
14         list.containsAll(list2);// 集合是否包含了另外一个集合的所有元素,若包含了,则返回true
15         list.indexOf("张三");// 集合是查询到第一次出现“张三”的索引位置,若集合中没有“张三”则返回-1
16         list.lastIndexOf("张三");// 集合中最后一次出现“张三”的索引位置,若没有“张三”则返回-1
17         list.isEmpty();// 集合是否没有任何元素,若是空集合则返回true
18         list.remove("张三");// 移除第一次出现的“张三”
19         list.remove(0);// 移除指定位置上的元素
20         list.removeAll(list2);// 移除所包含的集合元素(差集)
21         list.retainAll(list2);// 两个集合的并集
22         list.set(0, "马六");// 设置指定位置的值
23         list.size();// 集合的元素个数
24         list.subList(2, 4);// 返回list的子集,包括位置2但不包括4
25         Object[] obj = list.toArray();// 返回集合中所有的元素组成的数组
26         String[] ary = new String[list.size()];
27         list.toArray(ary);// 返回集合中所有的元素组成的数组,但是可以指定其返回的数组数据类型,即此方法的参数数组类型
28         list.clear();// 移除集合中所有的元素
29     }

   注:以上代码中用for循环取出集合中的元素方法是List集合所物有的方式

  6,List里的iterator和listIterator方法

   我们知道,用iterator方法可以获取一个迭代器对象,然后方便对集合中的元素进行循环的访问,现在有一个这样的需求,循环遍历集合中的元素,当元素为“张三”时,为集合添加一个元素,实现代码如下

 1     private static void function_demo3() {
 2         List list = new ArrayList();
 3         list.add("张三");// 将元素添加到列表的尾部
 4         list.add(0, "李四");// 将元素添加到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("赵六");
 8         list.addAll(list2);// 将一个集合中的元素添加到另一个集合中尾部
 9         list.addAll(0, list2);// 将一个集合中的元素添加到另一个集合中指定位置
10         Iterator it = list.iterator();//获取迭代器对象
11         while (it.hasNext()) {
12             String str = it.next().toString();
13             if (str.equals("张三")) {
14                 list.add("马七");
15             } else {
16                 System.out.println(str);
17             }
18         }
19     }

    程序运行结果:

      

  经调试发现,程序在运行到“张三”这个元素后为集合添加元素时,抛出了java.util.ConcurrentModificationException异常,此异常是当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。我们可以得出结果,就是iterator接口不允许我们在读取的时候进行修改操作。

  而listIterator方法返回一个ListIterator接口,此接口允许在进行迭代的时候进行添加删除修改的操作,并可按任一方向(前或后)进行遍历操作,演示代码如下

 1     private static void function_demo3() {
 2         List list = new ArrayList();
 3         list.add("张三");// 将元素添加到列表的尾部
 4         list.add(0, "李四");// 将元素添加到列表的指定位置
 5         List list2 = new ArrayList();
 6         list2.add("王五");
 7         list2.add("赵六");
 8         list.addAll(list2);// 将一个集合中的元素添加到另一个集合中尾部
 9         list.addAll(0, list2);// 将一个集合中的元素添加到另一个集合中指定位置
10         ListIterator it = list.listIterator();// 获取迭代器对象
11         while (it.hasNext()) {
12             String str = it.next().toString();
13             if (str.equals("张三")) {
14                 System.out.println(str);
15                 it.add("马七");//此处用的是ListIterator的对象it即调用的是ListIterator的方法进行添加操作
16             } else {
17                 System.out.println(str);
18             }
19         }
20         System.out.println(list.size());
21     }

      使用listIterator不但可以在迭代的同时进行增删改的操作,还可以回退着读即调用hasPrevious方法,此处不再演示

      注:此功能仅有List集合具备

 三,List常用的实现类
  Collection是集合框架的顶层接口,而List是Collection的子接口,我们开发中真正用到的是具体类里面的方法,所以介绍一下比较常用的List的实现方法还是很有必要的。
  List比较常用的方法有三个Vector,ArrayList,LinkedList,那么接下来我们先接着上面的那个类图来把这三个类添加上去,图例如下:
          

  1,List常用实现类Vector

    Vector类是个比较古老的类,从1.0版本便有了,也就是说此类是先于集合框架出现的,只是由于效率问题到后来并到List下面的。

  Vector类内部维护的是一个数组,此数组可动态扩展。若实例化该类时用的是无参构造则数组默认大小为10,当此容器内部的数量大于10时,容量则动态扩展一倍;也可在实例化该类时指定初始大小,并可指定增长量;演示如下:

 1     private static void function_demo4() {
 2         Vector v = new Vector();// 使用默认大小
 3         System.out.println("Vector默认容量:" + v.capacity());
 4         Vector v2 = new Vector(2);// 指定初始大小为2
 5         System.out.println("Vector默认容量:" + v2.capacity());
 6         v2.add(2);
 7         v2.add(3);
 8         System.out.println("元素个数:" + v2.size());
 9         System.out.println("Vector默认容量:" + v2.capacity());
10         v.add(3);
11         System.out.println("元素个数:" + v2.size());
12         System.out.println("Vector默认容量:" + v2.capacity());//此时元素个数超出来初始大小2,便动态扩展一倍的容量
13     }

     指定容量大小,并指定增长量大小演示如下:

 1     private static void function_demo5() {
 2         Vector v = new Vector(2, 1);// 默认大小为2,当超出后每次增长1
 3         v.add(2);
 4         v.add(3);
 5         System.out.println("元素个数:" + v.size());
 6         System.out.println("Vector默认容量:" + v.capacity());
 7         v.add(4);
 8         System.out.println("元素个数:" + v.size());
 9         System.out.println("Vector默认容量:" + v.capacity());
10     }

   Vector除了实现了List接口后重写了List里的方法,还维护着它自己之前的方法,这些方法不但名字较长而且效率低下,完成的功能与List里的方法功能相同,如addElement方法和add方法,elementAt与indexOf方法等等,其中有一个方法用于遍历容器中的元素elements返回一个枚举器对象Enumeration被iterator所替代。

2,List常用实现类LinkedList

LinkedList是List接口的链表列表体现,即它的内部是链表的数据结构,所以它增删时的速度比较快,另外此类是非线程安全的,即非同步。

LinkedList除了实现了List接口的方法外,还提供了一些方法,这些方法可模拟堆栈,队列,和双端队列的操作。

基本的增删改查的方法就不再演示了,我们用LinkedList所提供的可模拟堆栈,队列和双端队列的方法来完成一个自定义的堆栈及队列操作。

我们知道,堆栈有一个特点即后进先出而队列的特点是先进先出,基于此可有以下操作:

 1 public class DuiZhan {
 2     private LinkedList linkedList;
 3 
 4     public DuiZhan() {
 5         linkedList = new LinkedList();
 6     }
 7 
 8     public void add(Object e) {
 9         linkedList.push(e);
10     }
11 
12     public Object get() {
13         return linkedList.pollLast();
14     }
15 
16 }
 1 public class DuiLie {
 2     private LinkedList linkedList;
 3 
 4     public DuiLie() {
 5         linkedList = new LinkedList();
 6     }
 7 
 8     public void add(Object obj) {
 9         linkedList.push(obj);
10     }
11 
12     public Object get() {
13         return linkedList.pollFirst();
14     }
15 }

  3,List常用实现类ArrayList

  ArrayList类是开发中最常用的类,此类内部是数组的形式;功能上基本与Vector相类似,但它是不同步的,是Vector的替代品;由于内部是数组结构,所以用于查询速度相对较快。

  ArrayList低层所维护的数组的长度动态扩展大约每次扩展原来容量1.5倍的容量,但是ArrayList有一个方法ensureCapacity,此方法接收一个int类型的参数,这个方法的作用是当此方法所传递的参数为大于ArrayList默认容量的1.5倍时,ArrayList扩展为此参数大小的容量;如果此方法所传递的参数为小于ArrayList默认容量的1.5倍时,ArrayList扩展为默认值的1.5倍容量;这个容量的扩展与否关系着ArrayList执行效率的快慢问题,因为ArrayList扩容是新建一个数组,然后把原来数组中的内容复制到新数组中去,是很消耗资源的。下面用一个例子作以演示:

 1     private static void function_demo8() {
 2         int n = 100000;
 3         ArrayList al = new ArrayList();
 4         long startTime = System.currentTimeMillis();
 5         for (int i = 0; i < n; i++) {
 6             al.add(i);
 7         }
 8         System.out.print("默认扩展容量耗时:");
 9         System.out.println(System.currentTimeMillis() - startTime);
10         ArrayList al1 = new ArrayList();
11         al1.ensureCapacity(n);
12         startTime = System.currentTimeMillis();
13         for (int i = 0; i < n; i++) {
14             al1.add(i);
15         }
16         System.out.print("手动扩展容量耗时:");
17         System.out.println(System.currentTimeMillis() - startTime);
18     }

   程序运行结果:

        

 可以感受一下,这个差距还是很大的。另外ArrayList还有一个方法trimToSize,这个方法的作用是将容器的大小调整为与元素数量相同的大小,举例说明一下:假设ArrayList默认容量为10,当添加第11个元素的时候容量会扩展成15,但此时元素只有11个,另外的四个位置其实是浪费的,那么这个时候调用一下trimToSize方法会将多余的那四个位置给清除掉,具体不再演示,需要去扒源码。ArrayList的其它方法基本上没有什么难理解的了,就不再一一演示。

 

猜你喜欢

转载自www.cnblogs.com/zw971084570/p/10107130.html