zoukankan      html  css  js  c++  java
  • 集合(上)

    传统的容器(数组)在进行增、删等破坏性操作时,需要移动元素,可能导致性能问题;同时添加、删除等算法和具体业务耦合在一起,增加了程序开发的复杂度。Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中。

    1 Collection 接口

    Collection是java集合框架(collection-frame)中的顶层接口。Collection接口是一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操作。容器中的元素可以是有序的、可重复的,称为List接口;也可能是无序的、唯一的,称为Set接口。

    1.1 集合常用方法

     1 public static void main(String[] args) {
     2         
     3         /**
     4          * 增:add/addAll
     5          * 删:clear/remove/removeAll/retainAll
     6          * 改:
     7          * 查:contains/containsAll/isEmpty/size
     8          */
     9         
    10         Collection c1 = new ArrayList();
    11         
    12         // 追加
    13         c1.add("apple"); // Object object = new String("apple");
    14         // c1.add(1);         // Object object = new Integer(1); 
    15         c1.add("banana");
    16         System.out.println(c1);
    17         
    18         // 追加一个集合 
    19         Collection c2 = new ArrayList();
    20         c2.add("java");
    21         c2.add("c++");
    22         c1.addAll(c2);
    23         System.out.println(c1);
    24         
    25         System.out.println(c1.contains("apple"));
    26         c2.add("js");
    27         System.out.println(c1.containsAll(c2));
    28         // c1.clear();
    29         System.out.println(c1.isEmpty());
    30         // 返回集合元素的个数
    31         System.out.println(c1.size());
    32         
    33         System.out.println(c1.equals(c2));        
    34 }

    1.3 集合的遍历

    Iterable 是一个可遍历的接口,集合接口继承于它,集合支持快速遍历。

    for (Object item : c1) {
        System.out.println(item.toString());
    }

    快速遍历的本质:

    Collection 继承 Iterable 接口,表示集合支持快速遍历。Iterable 接口定义了一个方法iterator()用于获取集合的迭代器,是一个 Iterator 接口类型,iterator()内部返回一个实现类实现类 Iterator 接口。这个实现类一定具有 hasNext 和 next 方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。

     1 public static void main(String[] args) {
     2         
     3         Collection c1 = new ArrayList();
     4         c1.add("apple");
     5         c1.add("banana");
     6         c1.add("coco");
     7         
     8         // 快速遍历
     9         // for-each
    10         // Object 表示元素类型 
    11         // item表示迭代变量
    12         // c1表示集合
    13         for (Object item : c1) {
    14             System.out.println(item.toString());
    15         }
    16         
    17         // 方法1
    18         Iterator it = c1.iterator();
    19         while(it.hasNext()) {
    20             Object item = it.next();
    21             System.out.println(item.toString());
    22         }
    23         
    24         // 方法2(推荐)
    25         for(Iterator it2=c1.iterator();it2.hasNext();) {
    26             Object item = it2.next();
    27             System.out.println(item.toString());
    28         }    
    29 }

    查看源码可以看到 next 方法的实现

     1 @SuppressWarnings("unchecked")
     2         public E next() {
     3             checkForComodification();
     4             int i = cursor;
     5             if (i >= size)
     6                 throw new NoSuchElementException();
     7             Object[] elementData = ArrayList.this.elementData;
     8             if (i >= elementData.length)
     9                 throw new ConcurrentModificationException();
    10             cursor = i + 1;
    11             return (E) elementData[lastRet = i];
    12         }

    iterator 方法会返回一个私有类 Itr 的实例,该类中定义了一个 cursor 变量,初始值为 0,表示当前”光标“指向的元素索引;定义了一个 lastRet 变量初始值为 -1,表示上一个遍历过的元素的索引。每次使用 next 后,将 cursor 赋给 i ,光标 cursor 后移一位,同时返回当前 i 指向的元素,并将 i 赋给 lastRet。

    iterator 是线程不安全的,不支持在遍历的同时修改集合元素。每次使用 next 的时候,会首先使用 checkForComodification 方法,查看源码可知,该方法会判断两个变量 modcount、expectedModCount 是否相等,如果不相等就抛出“同时修改异常”。modcount 是 ArrayList 的父类 AbstractList 中定义的一个变量,Arraylist 的 add 方法每次执行时,会先调用 ensureCapacityInternal 方法判断容量并自动扩容,该方法又调用了 ensureExplicitCapacity 方法,该方法每次调用时 modcount 会自加一次。而expectedMoCount 是在创建 Itr 实例时生成的,将ArrayList 的 modcount 赋给它,所以当在遍历过程中修改集合元素,next 方法调用时就会抛出“同时修改异常”。

     1 private void ensureCapacityInternal(int minCapacity) {
     2         ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
     3     }
     4 
     5     private void ensureExplicitCapacity(int minCapacity) {
     6         modCount++;
     7 
     8         // overflow-conscious code
     9         if (minCapacity - elementData.length > 0)
    10             grow(minCapacity);
    11     }

    2 List 接口

    List 继承 Collection。List 接口中的元素时有序的、可重复的。List接口中的元素通过索引(index)来确定元素的顺序。可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

    2.1 List 常用方法

     1 public static void main(String[] args) {
     2         
     3         /**
     4          * 增:add/addAll/add(index,el)/addAll(index,collection)
     5          * 删:clear/remove/removeAll/remove(index)
     6          * 改:set(index,el)
     7          * 查:get(index)/indexOf/lastIndexOf()
     8          * 其他:contains/containsAll/isEmpty/size
     9          */
    10         List list1 = new ArrayList();
    11         // 添加元素
    12         list1.add("apple");
    13         list1.add("banana");
    14         // 在指定位置添加元素
    15         list1.add(0, "coco");
    16         
    17         System.out.println(list1);
    18         
    19         List list2 = new ArrayList();
    20         list2.add("java");
    21         list2.add("c++");
    22         
    23         list1.addAll(1, list2);
    24         System.out.println(list1);
    25         
    26         // 删除
    27         list1.remove(0);
    28         System.out.println(list1);
    29         
    30         // 修改
    31         list1.set(0, "javax");
    32         System.out.println(list1);
    33         
    34         //
    35         System.out.println(list1.get(0));
    36         list1.add("apple");
    37         list1.add("apple");
    38         System.out.println(list1);
    39         System.out.println(list1.indexOf("apple"));
    40         System.out.println(list1.lastIndexOf("apple"));
    41 }

    2.2 List 接口的遍历

    ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也可以以逆序遍历集合。hasNext/next 以正向遍历;hasPrevious/previous 以逆序遍历。

     1 public static void main(String[] args) {
     2     
     3         List list1 = new ArrayList();
     4         list1.add("apple");
     5         list1.add("banana");
     6         list1.add("coco");
     7         
     8         // 【1】快速遍历
     9         System.out.println("--for each--");
    10         for (Object item : list1) {
    11             System.out.println(item.toString());
    12         }
    13         
    14         // 【2】普通for
    15         System.out.println("--for--");
    16         for(int i=0;i<list1.size();i++) {
    17             System.out.println(list1.get(i));
    18         }
    19         
    20         // 【3】集合迭代器
    21         System.out.println("--iterator--");
    22         Iterator it = list1.iterator();
    23         while(it.hasNext()) {
    24             System.out.println(it.next());
    25         }
    26         
    27         System.out.println("--list iterator--");
    28         // 正向遍历
    29         ListIterator it2 = list1.listIterator();
    30         while(it2.hasNext()) {
    31             System.out.println(it2.next());
    32         }
    33         
    34         // 逆序遍历
    35         while(it2.hasPrevious()) {
    36             System.out.println(it2.previous());
    37         }
    38         
    39         System.out.println("--list iterator with index--");
    40         ListIterator it3 = list1.listIterator(1);
    41         while(it3.hasNext()) {
    42             System.out.println(it3.next());
    43         }
    44 }

    3 ArrayList/Vector

    ArrayList 是List接口的实现类,底层数据结构是数组,实现大小可变的数组。ArrayList 线程不安全,从 jdk1.2 开始使用。ArrayList 底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2;如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。ArrayList作为List接口的实现类,常用方法和遍历方法参考List接口。

    Vector 是List接口的实现类,底层数据结构也是数组,也是大小可变的数组。Vector是线程安全的,从 jdk1.0 开始使用。Vector底层数据结构是数组,默认数组大小是10,如果添加的元素个数超过默认容量,Vector会自动拓容,拓容原则:newCapacity = oldCapacity +capacityIncrement(增长因子);如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。

    注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement,未来使用时为了程序的可拓展性,一定要按照接口来操作Vector。

    4 LinkedList

    LinkedList是List接口的实现类,底层数据结构是链表。LinekList常用方法和遍历方法参照List接口。LinkedList 线程不安全。

    除了实现List接口, 还实现了栈接口(后进先出 LIFO),通过 push 和 pop 方法实现栈的操作。

     1 public static void main(String[] args) {
     2         LinkedList list = new LinkedList();
     3         list.push("apple");
     4         list.push("banana");
     5         list.push("coco");
     6         
     7         
     8         System.out.println(list.pop()); //coco
     9         System.out.println(list.pop()); //banana
    10         System.out.println(list.pop());
    11         
    12         // java.util.NoSuchElementException
    13         System.out.println(list.pop());
    14 }

    也实现了队列接口(先进先出 FIFO),提供两套方法实现。

    add/remove/element() 可能会出现NoSuchElementException异常
     1 public static void main(String[] args) {
     2         
     3         LinkedList queue = new LinkedList();
     4         // 入队
     5         /**
     6          * 队列头                          队列尾
     7          *<-----          <-----
     8          * [apple, banana, coco]
     9          */
    10         queue.add("apple");
    11         queue.add("banana");
    12         queue.add("coco");
    13         System.out.println(queue);
    14         
    15         // 出队
    16         System.out.println(queue.remove()); //返回队列头元素,并从队列中移除
    17         System.out.println(queue.remove());
    18         System.out.println(queue.remove());        
    19         System.out.println(queue);
    20         
    21         // java.util.NoSuchElementException
    22         System.out.println(queue.remove());
    23         
    24         
    25         // 获取表头元素
    26         System.out.println(queue.element());
    27 }
    offer/poll/peek 可能会返回特殊值(null)
     1 public static void main(String[] args) {
     2         
     3         LinkedList queue = new LinkedList();
     4         // 入队
     5         /**
     6          * 队列头                          队列尾
     7          *<-----          <-----
     8          * [apple, banana, coco]
     9          */
    10         queue.offer("apple");
    11         queue.offer("banana");
    12         queue.offer("coco");
    13         
    14         // 出队列
    15         //System.out.println(queue.poll());
    16         //System.out.println(queue.poll());
    17         //System.out.println(queue.poll());
    18         System.out.println(queue);
    19 
    20         //System.out.println(queue.poll());//输出 null
    21         
    22         // 获取表头元素
    23         System.out.println(queue.peek());
    24     
    25 }

    同时也继承了双向队列接口,两头可进可出,一样提供两套方法,一个会抛异常,一个会返回 null。

    用法如上面代码类似。

    5 ListIterator

    正如上文讲遍历时所说的,Iterator 不支持遍历的过程中修改集合元素,而 ListIterator 正好弥补了这个缺陷,ArrayList 对象可以使用 listIterator 方法获得一个 ListIterator 的实例。ListIterator允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。

     1 public static void main(String[] args) {
     2         ArrayList list = new ArrayList();
     3         list.add("apple");
     4         list.add("banana");
     5         list.add("coco");
     6         
     7         ListIterator it = list.listIterator();
     8         while(it.hasNext()) {
     9             String item = (String) it.next();
    10             if(item.equals("banana")) {
    11                 it.add("test");  
    12                         //在当前光标(cursor)位置插入一个元素,这个 add 方法是属于ListIterator 的
    13                         // next 方法调用后,光标 cursor 会后移
    14             }
    15         }
    16         
    17         System.out.println(list); //[apple, banana, test, coco]
    18 }

    6 泛型(generic)

    6.1 概念

    泛型允许开发者在强类型程序设计语言(java)编写代码时定义一些可变部分,这些部分在使用前必须作出指明。泛型就是将类型参数化。

    • ArrayList<E> list表示声明了一个列表list,列表的元素是E类型
    • ArrayList<String> list = new ArrayList<String>();声明了一个列表list,列表的元素只能是String类型。

    6.2 泛型的擦除

    泛型在编译器起作用,运行时jvm察觉不到泛型的存在。泛型在运行时已经被擦除了。

    1 public static void main(String[] args) {
    2         ArrayList<String> list = new ArrayList<String>();
    3         list.add("apple");
    4         System.out.println(list instanceof ArrayList); //true
    5         System.out.println(list instanceof ArrayList<String>);
    6                //Cannot perform instanceof check against parameterized type ArrayList<String>.
    7                // Use the form ArrayList<?> instead since further generic type information will 
    8                //be erased at runtime
    9 }

    6.3 泛型的应用

    泛型类

    当一个类中属性的数据类型不确定时,具体是什么类型由使用者来确定时,使用泛型。泛型类的形式:

    1 public class 类名<T> {
    2     
    3 }

    定义一个泛型类:

     1 class FanClass<T> {
     2     private T t;
     3 
     4     public T getT() {
     5         return t;
     6     }
     7 
     8     public void setT(T t) {
     9         this.t = t;
    10     }
    11 
    12     public FanClass(T t) {
    13         super();
    14         this.t = t;
    15     }
    16 
    17     public FanClass() {
    18         super();
    19     }
    20 }
    21 public class Test01 {
    22     public static void main(String[] args) {
    23         FanClass<String> fan = new FanClass<String>();
    24         fan.setT("apple");
    25         
    26         FanClass<Integer> fan2 = new FanClass<Integer>();
    27         fan2.setT(1);
    28     }
    29 }

    泛型方法

    当一个方法的参数类型不确定时,具体是什么类型由使用者来确定,可以考虑使用泛型方法,泛型方法在调用时确定(指明)类型。形式:

    1 public <T> void xxx(T a) {
    2     System.out.println(a);
    3 }

    举例:

     1 class Student {
     2     public <T> void showInfo(T a) {
     3         System.out.println(a);
     4     }
     5 }
     6 public class test {
     7         public static void main(String[] args) {
     8         Student stu = new Student();
     9         stu.showInfo(1);
    10         stu.showInfo("apple");
    11         stu.showInfo(1.0f);  //传入的参数是什么类型,T 就是什么类型
    12     }
    13 }

    泛型接口

    如果接口中的方法的参数(形参、返回值)不确定时,可以考虑使用泛型接口。形式:

    1 public interface FanInterface<T> {
    2     public void showInfo(T t);
    3 }
    • 实现类能确定泛型接口的类型
    1 public class ImplClass implements FanInterface<String>{
    2 
    3     @Override
    4     public void showInfo(String t) {
    5         // TODO Auto-generated method stub
    6         
    7     }
    8 }
    • 实现类不能确定泛型接口的类型->继续泛。
    1 public class ImplClass2<T> implements FanInterface<T>{
    2 
    3     @Override
    4     public void showInfo(T t) {
    5         
    6     }
    7 }

    6.4 泛型的上、下限

    1 public static void print(ArrayList<? extends Pet> list) {
    2         for (Pet pet : list) {
    3             pet.showInfo();
    4         }
    5 }
    • 泛型的上限ArrayList(? extends Pet) list 声明了一个容器,容器中的元素类型一定要继承于Pet,我们称这种形式叫做泛型的上限。
    • 泛型的下限ArrayList(? super Pet) list 声明了一个容器,容器中的元素类型一定要是Pet的父类,我们称这个形式为泛型的下限。
  • 相关阅读:
    在线程中使用OpenFileDialog
    Log4net附加
    SQL表间列根据条件复制(赋值)
    DataGridView使用
    Latex使用总结及备忘
    Windows获取文件状态
    TabControl取消首次定位焦点
    C#跨线程调用
    电子词典的查寻程序,发送和接收应答程序
    电子词典的相关子函数db.c程序
  • 原文地址:https://www.cnblogs.com/carlosouyang/p/10804178.html
Copyright © 2011-2022 走看看