• Java SE 核心 II【Collection 集合框架】


    Collection集合框架

     


      在实际开发中,需要将使用的对象存储于特定数据结构的容器中。而 JDK 提供了这样的容器——集合框架,集合框架中包含了一系列不同数据结构(线性表、查找表)的实现类。
    集合的引入:
    数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从效率和类型检查的角度讲,数组是最好的。
    数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。
    1)Collection 常用方法:
    ①int size():返回包含对象个数。 ②boolean isEmpty():返回是否为空。
    ③boolean contains(Object o):判断是否包含指定对象。
    ④void clear():清空集合。 ⑤boolean add(E e):向集合中添加对象。
    ⑥boolean remove(Object o):从集合中删除对象。
    ⑦boolean addAll(Collection<? extends E > c):另一个集合中的所有元素添加到集合
    ⑧boolean removeAll(Collection<?> c):删除集合中与另外一个集合中相同的原素
    ⑨Iterator<E> iterator():返回该集合的对应的迭代器
    2)Collection 和 Collentions 的区别
      Collection 是 java.util 下的接口,它是各种集合的父接口,继承于它的接口主要有 Set和 List;
      Collections 是个 java.util 下的类,是针对集合的帮助类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
    1、List集合的实现类(ArrayList、LinkedList和Vector
    List 接口是 Collection 的子接口,用于定义线性表数据结构,元素可重复、有序的;可以将 List 理解为存放对象的数组,只不过元素个数可以动态的增加或减少。
    1)List 接口的两个常见的实现类:ArrayList 和 LinkedList,分别用动态数组和链表的方式实现了 List 接口。List、ArrayList 和 LinkedList 均处于 java.util 包下。
    2)可以认为 ArrayList 和 LinkedList 的方法在逻辑上完全一样,只是在性能上有一定的差别,ArrayList 更适合于随机访问,而 LinkedList 更适合于插入和删除,在性能要求不是特别苛刻的情形下可以忽略这个差别。
    List接口常用的实现类有3个:ArrayList、LinkedList和Vector。


    ArrayList查询效率高,增删效率低,线程不安全。我们一般使用它

      1 package boom.collection;
      2 
      3 import java.util.ArrayList;
      4 import java.util.Collection;
      5 import java.util.Iterator;
      6 import java.util.List;
      7 import java.util.ListIterator;
      8 
      9 /**
     10  * 测试collection接口的方法 List接口
     11  * 
     12  * @author Administrator
     13  *
     14  */
     15 public class ListTest {
     16 
     17     public static void main(String[] args) {
     18          //test01();
     19         //test02();
     20         test03();
     21 
     22     }
     23 
     24     /**
     25      * 集合带索引顺序的相关方法
     26      */
     27     public static void test03() {
     28         List<String> list = new ArrayList<>();
     29         
     30         // 添加元素
     31         list.add("京东");
     32         list.add("阿里");
     33         list.add("腾讯");
     34         list.add("百度");
     35         System.out.println(list);// [京东, 阿里, 腾讯, 百度]
     36         
     37         // 在指定索引位置插入新元素:index[0]
     38         list.add(0, "网易");
     39         System.out.println(list);// [网易, 京东, 阿里, 腾讯, 百度]
     40         
     41         // 移除指定位置的元素:index[3]
     42         list.remove(3);
     43         System.out.println(list);// [网易, 京东, 阿里, 百度]
     44         
     45         // 指定位置更改元素:index[0]
     46         list.set(0,"Google");
     47         System.out.println(list);// [Google, 京东, 阿里, 百度]
     48         // 获得更改的元素
     49         System.out.println(list.get(0));// Google
     50         
     51         
     52         list.add("阿里");
     53         System.out.println(list);// [Google, 京东, 阿里, 百度, 阿里]
     54         // 顺序查找(角标0开始)指定的元素下标
     55         System.out.println(list.indexOf("阿里"));// 2
     56         // 从后往前找
     57         System.out.println(list.lastIndexOf("阿里"));// 4
     58         
     59             
     60     }
     61 
     62     /**
     63      * ArrayList_操作多个List_并集和交集及两个list之间操作元素
     64      */
     65     public static void test02() {
     66         List<String> list01 = new ArrayList<String>();
     67         list01.add("AAA");
     68         list01.add("BBB");
     69         list01.add("CCC");
     70         
     71         List<String> list02 = new ArrayList<String>();
     72         list02.add("DDD");
     73         list02.add("CCC");
     74         list02.add("EEE");
     75         
     76         // 打印list01集合元素内容
     77         System.out.println(list01); // [AAA, BBB, CCC]
     78         
     79         // 把list02集合里所有的元素都添加到list01集合里:在末尾进行添加
     80         list01.addAll(list02);
     81         System.out.println(list01); // [AAA, BBB, CCC, DDD, CCC, EEE]
     82         
     83         // 把list02集合里的所有元素添加到01集合里指定的位置,根据索引
     84         list01.addAll(2, list02);
     85         System.out.println(list01);// [AAA, BBB, DDD, CCC, EEE, CCC, DDD, CCC, EEE]
     86         
     87         // 01集合里是否包含02集合的所有元素 :true or false
     88         System.out.println(list01.contains(list02)); // false
     89         
     90         
     91         // 先进行打印,方便看效果
     92         System.out.println("list01:"+list01); // list01:[AAA, BBB, DDD, CCC, EEE, CCC, DDD, CCC, EEE]
     93         System.out.println("list02:"+list02); // list02:[DDD, CCC, EEE]
     94         
     95         //移除集合01和集合02中都包含的元素,返回01集合
     96         //list01.removeAll(list02);
     97         //System.out.println(list01);// [AAA, BBB]
     98         
     99         // 取本集合01和集合02中都包含的元素,移除非交集元素,返回01集合
    100         list01.retainAll(list02);
    101         System.out.println(list01); // [DDD, CCC, EEE, CCC, DDD, CCC, EEE]
    102         
    103     }
    104 
    105     /**
    106      * List接口常用方法
    107      */
    108     public static void test01() {
    109         List<String> list = new ArrayList<String>();
    110         // 判断集合是否为空
    111         System.out.println(list.isEmpty());// true
    112         
    113         // 添加add,按下标顺序添加
    114         list.add("国产001");
    115         list.add("国产002");
    116         list.add("国产003");
    117         
    118         System.out.println(list.isEmpty());// false
    119         
    120         // 打印 list自动调用toString方法
    121         System.out.println(list.toString()); // [国产001, 国产002, 国产003]
    122         System.out.println("集合的长度:" + list.size());// 集合的长度:3
    123 
    124         // 指定位置索引添加元素
    125         list.add(0, "new");// 在下标0的位置添加新元素 "new"
    126         System.out.println(list); // [new, 国产001, 国产002, 国产003]
    127 
    128         // 移除
    129         list.remove(0);// 移除下标为0的元素
    130         System.out.println(list);// [国产001, 国产002, 国产003]
    131         
    132         // 根据下标修改某个元素的值
    133         // index[1]的位置更改为 "改"
    134         list.set(1, "改");
    135         System.out.println(list);// [国产001, 改, 国产003]
    136 
    137         // 测试集合是否包含指定的元素:true or false
    138         System.out.println("是否包含指定元素:" + list.contains("国产"));// false
    139         System.out.println("是否包含指定元素:" + list.contains("国产003"));// true
    140 
    141         // 清空集合所有的元素
    142         list.clear();
    143         System.out.println("清空所有元素:" + list); // 清空所有元素:[]
    144 
    145     }
    146 }
    ListTest Code

    运行效果参照图:

    ArrayList_底层JDK源码解读

    ArrayList:底层是用可变长度数组实现的存储。
    特点:查询效率高,增删效率低,线程不安全。

    1、数组长度是有限的,而ArrayList是可以存放任意数量的对象,长度不受限制。
    2、那么它是怎么实现的呢?
      本质上就是通过定义新的更大的数组,将旧数组中的内容拷贝到新数组,来实现扩容。 ArrayList的Object数组初始化长度为10,如果我们存储满了这个数组,需要存储第11个对象,就会定义新的长度更大的数组,并将原数组内容和新的元素一起加入到新数组中。
    LinkedList:底层用双向链表实现的存储。
    特点:查询效率低,增删效率高,线程不安全。


    Vector:Vector底层是用数组实现的List,相关的方法都加了同步检查,因此“线程安全,效率低”

    2、泛型
    1)泛型是 JDK1.5 引入的新特性,泛型的本质是参数化类型。在类、接口、方法的定义过程中,所操作的数据类型为传入的指定参数类型。所有的集合类型都带有泛型参数,这样在创建集合时可以指定放入集合中的对象类型。同时,编译器会以此类型进行检查。
    2)ArrayList 支持泛型,泛型尖括号里的符号可随便些,但通常大写 E。
    3)迭代器也支持泛型,但是迭代器使用的泛型应该和它所迭代的集合的泛型类型一致!
    4)泛型只支持引用类型,不支持基本类型,但可以使用对应的包装类
    5)如果泛型不指定类型的话,默认为 Object 类型。
    3、List 高级-数据结构:Queue 队列
      队列(Queue)是常用的数据结构,可以将队列看成特殊的线性表,队列限制了对线性表的访问方式:只能从线性表的一端添加(offer)元素,从另一端取出(poll)元素。Queue接口:在包 java.util.Queue。
    1)队列遵循先进先出原则:FIFO(First Input First Output)队列不支持插队,插队是不道德的。
    2)JDK 中提供了 Queue 接口,同时使得 LinkedList 实现了该接口(选择 LinkedList 实现Queue 的原因在于 Queue经常要进行插入和删除的操作,而 LinkedList 在这方面效率较高)。
    4、List 高级-数据结构:Deque 栈
      栈(Deque)是常用的数据结构,是 Queue 队列的子接口,因此 LinkedList 也实现了 Deque接口。栈将双端队列限制为只能从一端入队和出队,对栈而言即是入栈和出栈。例如:子弹夹就是一种栈结构。在包 java.util.Deque 下。
    1)栈遵循先进后出的原则:FILO(First Input Last Output)。
    2)常用方法:
    ①push:压入,向栈中存入数据。
    ②pop:弹出,从栈中取出数据。
    ③peek:获取栈顶位置的元素,但不取出
    5、Set集合的实现类(HashSet 和 TreeSet )
    Set容器特点:无序、不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;甚至,Set中也只能放入一个null元素,不能多个。
    1)HashSet 和 TreeSet 是 Set 集合的两个常见的实现类,分别用 hash 表和排序二叉树的方式实现了 Set 集合。HashSet 是使用散列算法实现 Set 的。
    2)Set 集合没有 get(int index)方法,我们不能像使用 List 那样,根据下标获取元素。想获取元素需要使用 Iterator。
    3)向集合添加元素也使用 add 方法,但是 add 方法不是向集合末尾追加元素,因为无序。
    HashSet:采用哈希算法实现,底层实际是用HashMap实现的(HashSet本质就是一个简化版的HashMap),因此,查询效率和增删效率都比较高。
    TreeSet:底层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap,通过key来存储Set的元素。
    (1) 由于是二叉树,需要对元素做内部排序。 如果要放入TreeSet中的类没有实现Comparable接口,则会抛出异常:java.lang.ClassCastException。
    (2) TreeSet中不能放入null元素。
    6、Map集合的实现类(HashMap、TreeMap、HashTable、Properties等)
      Map就是用来存储“键(key)-值(value) 对”的。 Map类中存储的“键值对”通过键来标识,所以Key 不能重复,但所保存的 Value 可以重复。
    HashMap和HashTable
    HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于底层采用了哈希表存储数据,键不能重复,如果发生重复(是否重复通过equals方法),新的键值对会替换旧的键值对。
    特点:HashMap在查找、删除、修改方面都有非常高的效率。
    HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不过HashTable的方法添加了synchronized关键字确保线程同步检查,效率较低。
    HashMap与HashTable的区别
    1. HashMap:线程不安全,效率高。允许key或value为null。
    2. HashTable:线程安全,效率低。不允许key或value为null。
    TreeMap使用和底层原理_Comparable接口_HashTable特点(HashTable: 线程安全,效率低。不允许key或value为null。)
    TreeMap是红黑二叉树的典型实现。我们打开TreeMap的源码,发现里面有一行核心代码:private transient Entry<K,V> root = null;
    TreeMap和HashMap实现了同样的接口Map,用法对于调用者来说没有区别。HashMap效率高于TreeMap;在需要排序(comparable)的Map时才选用TreeMap。

    Collection集合框架的总结:
    1. Collection 表示一组对象,它是集中、收集的意思,就是把一些数据收集起来。
    2. Collection接口的两个子接口:
      1) List中的元素有顺序,可重复。常用的实现类有ArrayList、LinkedList和 vector。
        Ø ArrayList特点:查询效率高,增删效率低,线程不安全。
        Ø LinkedList特点:查询效率低,增删效率高,线程不安全。
        Ø vector特点:线程安全,效率低,其它特征类似于ArrayList。
      2) Set中的元素没有顺序,不可重复。常用的实现类有HashSet和TreeSet。
        Ø HashSet特点:采用哈希算法实现,查询效率和增删效率都比较高。
        Ø TreeSet特点:内部需要对存储的元素进行排序。因此,我们对应的类需要实现Comparable接口。才能根据compareTo()方法比较对象之间的大小,才能进行内部排序。
    3. 实现Map接口的类用来存储键(key)-值(value) 对。Map 接口的实现类有HashMap和TreeMap等。Map类中存储的键-值对通过键来标识,所以键值不能重复。
    4. Iterator对象称作迭代器,用以方便的实现对容器内元素的遍历操作。
    5. 类 java.util.Collections 提供了对Set、List、Map操作的工具方法。
    6. 如下情况,可能需要我们重写equals/hashCode方法:
      1) 要将我们自定义的对象放入HashSet中处理。
      2) 要将我们自定义的对象作为HashMap的key处理。
      3) 放入Collection容器中的自定义对象后,可能会调用remove、contains等方法时。
    7. JDK1.5以后增加了泛型。泛型的好处:
       1) 向集合添加数据时保证数据安全。
       2) 遍历集合元素时不需要强制转换。

    迭代器遍历Iterator【List、Set、Map】

  • 相关阅读:
    汤圆只有汤没有圆了,过了过了
    CommonJS
    What is Socket.IO?
    白话PGP/GPG
    汤圆只有汤没有圆了,过了过了
    npm for nodejs
    Running Daemontools under Ubuntu 8.10
    ubuntu seahouse
    node.js入门
    my blog zen :分享所学,backup一切~
  • 原文地址:https://www.cnblogs.com/cao-yin/p/9608250.html
Copyright © 2020-2023  润新知