Java 常见类(二)-------- 集合类


一。 基本概念

 1.1. Collection 集合接口:java.util.Collection 接口,List  、Set、Queue(队列)接口的超类接口

     1.1.1. List 接口 :有序,可重复。只关心索引,与其他集合相比,List特有的就是和索引相关的一些方法:get(int index) 、 add(int index,Object o) 、 indexOf(Object o) 。

        List 所代表的是有序的 Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

        ArrayList :一个动态数组,它允许所有元素,包括null。往 lis t集合里插入或删除数据时,会伴随着后面数据内存的移动,所有插入删除数据速度慢。异步,非线程安全的,运行速度快;如果想要线程安全的 ArrayList,可以通过Collections类的静态方法 synchronizeLlist获得线程安全的 ArrayList;

eg:List lst = Collections. synchronizedList (new ArrayList())。

        每个 ArrayList 实例都有一个初始容量(10),该容量是指用来存储列表元素的数组的大小. 它总是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长,在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。在添加大量元素前,应用程序可以使用 ensureCapacity  操作来增加 ArrayList 实例的容量,这可以减少递增式再分配的数量。

       ArrayList擅长于随机访问。同时ArrayList是非同步的。

        LinkedList元素之间是双链接的列表,允许null元素。查询慢,增加删除快。因为按照序号索引数据需进行向前或向后遍历,而插入时只需记录本项前后项。

        除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾getremoveinsert 元素提供了统一的命名方法。这些操作允许将 LinkedList 用作堆栈(stack),队列(queue)或双向队列(deque)。此类还实现了Deque 接口,为addpoll 提供先进先出队列操作,以及其他堆栈和双端队列操作。

          异步,为防止对列表进行意外的不同步访问,应该使用  Collections.synchronizedList  方法来“包装”该列表,如下:

eg:List list = Collections.synchronizedList (new LinkedList(...));

       (一般使用ArrayList。用LinkedList构造堆栈stack、队列queue。)

        Vector ArrayList 的线程安全版本,性能比ArrayList要低,现在已经很少使用。

     Stack : Stack继承自Vector,实现一个后进先出的堆栈。首次创建堆栈时,它不包含项。

         Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的 push 方法(把项压入堆栈顶部) 和 pop 方 法( 移除堆栈顶部的对象,并返回该对象作为此函数的值),还有 peek 方法得到栈顶的元素,empty 方法测试堆栈是否为空,search 方法检测一个元素在堆栈中的位置,以 1 为基数。如果对象o 是堆栈中的一个项,此方法返回距堆栈顶部最近的出现位置到堆栈顶部的距离;堆栈中最顶部项的距离为1,返回值-1 表示此对象不在堆栈中。(使用 equals 方法比较 o 与堆栈中的项)。

    1.1.2. Set 接口 : 无序的,关注事物的唯一性 , 不可重复,最多有一个 null 元素。

        HashSet 当不希望集合中有重复值,并且不关心元素之间的顺序时可以使用此类。HashSet 不是同步的,需要进行同步转换:
    eg.     Set s = Collections.synchronizedSet (new HashSet(...));

        HashSet   堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变。

        TreeSet基于TreeMap,生成一个总是处于排序状态的set,内部以TreeMap来实现。它是使用元素的自然顺序对元素进行排序,或者根据创建Set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。(自然顺序:和插入顺序无关,而是和元素本身的内容和特质有关的排序方式,譬如 “abc” 排在 “abd” 前面。)

        LinkedHashset当不希望集合中有重复值,并且希望按照元素的  插入顺序  进行迭代遍历时可采用此类

        EnumSet : 枚举专用Set。所有的元素都是枚举类型。

     1.1.3. Queue 接口: 在处理元素前用于保存元素的 collection,除了基本的Collection 操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(nullfalse,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的Queue 实现设计的,在大多数实现中,插入操作不会失败。

      eg : add() 和 offer() 都可以将指定的元素插入某队列,添加成功都返回 true;但是 添加失败时,add() 方法只能通过 抛出 未经检查的 异常 使添加元素失败, 如:当前没有可用的空间,则抛出 IllegalStateException;而 offer 方法 设计用于正常的失败情况,而不是出现异常的情况,例如在容量固定(有界)的队列中,添加失败,返回 false。

              remove() 和 poll() 方法均可移除和返回队列的头,但是,若队列为空时,poll() 方法则返回null,而remove() 方法抛出 NoSuchElementException.

    element()和 peek()方法均可返回但不移除队列的头,若队列为空,peek()方法返回null,element()方法抛出 NoSuchElementException.

             队列的链表实现是通过子类LinkedList来实现的:

  eg:  Queue queue = new LinkedList();
    Queue 实现通常不允许插入 null 元素,即使在允许 null 的实现中,也不应该将null 插入到Queue 中,因为null 也用作poll 方法的一个特殊返回值,表明队列不包含元素;同时,Queue接口收窄了LinkedList的访问权限,只提供从队尾,队头等的操作。

             队列通常(但并非一定)以 FIFO(先进先出)的方式排序各个元素。不过优先级队列和 LIFO 队列(或堆栈)例外,前者根据提供的比较器或元素的自然顺序对元素进行排序,后者按 LIFO(后进先出)的方式对元素进行排序。无论使用哪种排序方式,队列的 都是调用remove() poll() 所移除的元素。在 FIFO 队列中,所有的新元素都插入队列的末尾。其他种类的队列可能使用不同的元素放置规则。每个Queue 实现必须指定其顺序属性。


 1.2. Map 接口:关注事物的映射和键值的唯一性 ,将唯一的键映射到某个元素。键唯一,值可重复。键 和 值 都是对象。 Map 接口由 Map 的内容提供3种类型的集合视图,一组key集合,一组value集合,或者一组 key-value 映射关系的集合。

         HashMap最常用的Map集合,在Map 中插入、删除和定位元素,HashMap 都是最好的选择,非线程安全。如果想要线程安全的HashMap,可以通过 Collections 类的静态方法 synchronizedMap 获得线程安全的 HashMap。

  eg.    Map map = Collections.synchronizedMap (new HashMap()) ;

        HashMap 键值对 在存储时要根据 键 的 哈希码 来确定值放在哪里,遍历时,取得数据的顺序是完全随机的。允许 空键值  作为一个表的条目的 key 或 value. 

        HashMap 的底层主要是基于数组和链表来实现的,它之所以有相当快的查询速度主要是因为它是通过计算散列码来决定存储的位置。HashMap 中主要是通过 key 的 hashCode 来计算 hash 值的,只要 hashCode 相同,计算出来的 hash值 就一样。如果存储的对象对多了,就有可能不同的对象所算出来的hash值是相同的,这就出现了所谓的 hash 冲突, HashMap 底层是通过链表来解决hash 冲突的。

          图中,紫色部分即代表哈希表,也称为哈希数组,数组的每个元素都是一个单链表的头节点,链表是用来解决冲突的,如果不同的 key 映射到了数组的同一位置处,就将其放入单链表中。

     HashMap 内部类 Entry 类的代码:

     /** Entry是单向链表。    
     * 它是 “HashMap链式存储法”对应的链表。    
     *它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数  ;每个Map.Entry其实就是一个 key-value 对
      **/  
           static class Entry<k,v> implements Map.Entry<k,v> {
            final K key;
            V value;
            Entry<k,v> next;
             int hash;
             //.....
        }</k,v></k,v></k,v>

      在 Entry 中, 主要有4个属性,key ,hash,value, next(指向下一个节点的引用)。HashMap 其实就是一个 Entry 数组,Entry 对象中包含了键和值,其中 next 也是一个 Entry 象,它就是用来处理 hash 冲突的,形成一个链表。

         Hashtable 继承自 Dictionary类,它是 HashMap 的  线程安全  版本,不允许 空键值。现在已经很少使用。(注意Hashtable中的t是小写的)

         TreeMap: 键以某种排序规则排序,内部以red-black(红-黑)树数据结构实现,实现了 SortedMap 接口。当需要键值对,并关心元素的  自然排序  时可采用它。

         LinkedHashMap 当需要键值对,并且关心  插入顺序  时可采用它。

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


 1.3. 集合 工具类

     1.3.1. java.util.Collections 类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。常用方法 : Collections.sort( )  根据元素的自然顺序 对指定列表按升序进行排序 ; Collections.reverse()  反转指定列表中元素的顺序; Collections.fill() 使用指定元素替换指定列表中的所有元素;Collections.copy() 将所有元素从一个列表复制到另一个列表,执行此操作后,目标列表中每个已复制元素的索引将等同于源列表中该元素的索引。目标列表的长度至少必须等于源列表。如果目标列表更长一些,也不会影响目标列表中的其余元素。

      1.3.2. java.util.Arrays 类,它包含用来操作数组(比如排序和搜索)的各种方法,还包含一个允许将数组作为列表来查看的静态工厂。eg.  Arrays.asList( ) 返回一个受指定数组支持的固定大小的列表。


二。常用方法

 2.1. Collection 接口:get(); add();remove(index); remove(object);clear(); size();isEmpty();set()

 2.2. Map 接口:put(k,v); get();remove(k);clear(); size();isEmpty();

         特别关注:

         keySet():返回该Map中所有key所组成的set集合(key总是独一无二的);

         values():返回该Map里所有 value组成的Collection (value允许重复);

         entrySet (  ) :返回Map中所有包含的 key-value对 组成的 Set 集合,每个集合元素都是 Map.Entry (Map.Entry 是 Map 的内部接口)对象。


三。异同点

  3.1. Vector 和 ArrayList

      1、vector 是线程同步的,所以它也是线程安全的,而 arraylist 是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用 arraylist 效率比较高。
      2、如果集合中的元素的数目大于目前集合数组的长度时,vector 增长率为目前数组长度的100%,而 arraylist 增长率为目前数组长度的50%.如过在集合中使用数据量比较大的数据,用 vector 有一定的优势。
      3、如果查找一个指定位置的数据,vector 和 arraylist 使用的时间是相同的,都是 0(1),这个时候使用 vector 和 arraylist 都可以。而如果移动一个指定位置的数据花费的时间为 0(n-i)n 为总长度,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据所花费的时间为 0(1),而查询一个指定位置的数据时花费的时间为 0(i)。

      ArrayList 和 Vector 是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要设计到数组元素移动等内存操作,所以索引数据快插入数据慢,Vector 由于使用了 synchronized 方法(线程安全)所以性能上比 ArrayList 要差,LinkedList 使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快!

   3.2、Aarraylist 和 Linkedlist

      1、ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。
      2、对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
      3、对于新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList 的速度反而优于 LinkedList。但若是批量随机的插入删除数据,LinkedList 的速度大大优于ArrayList. 因为 ArrayList 每插入一条数据,要移动插入点及之后的所有数据。

     3.3、HashMap 和 TreeMap

       1、HashMap 通过 hashcode 对其内容进行快速查找,而 TreeMap 中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用 TreeMap(HashMap中元素的排列顺序是不固定的)。HashMap 中元素的排列顺序是不固定的)。

       2、HashMap 通过 hashcode 对其内容进行快速查找,而 TreeMap 中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用 TreeMap(HashMap中元素的排列顺序是不固定的)。集合框架”提供两种常规的Map实现:HashMap和 TreeMap ( TreeMap 实现 SortedMap 接口)。

       3、在Map 中插入、删除和定位元素,HashMap 是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了 hashCode() 和  equals() 的实现。 这个TreeMap没有调优选项,因为该树总处于平衡状态。

     3.4、Hashtable  和 HashMap

        1、历史原因: Hashtable 是基于陈旧的 Dictionary 类的,HashMap 是 Java 1.2引进的 Map接口的一个实现 。

      2、同步性: Hashtable 是线程安全的,也就是说是同步的,而 HashMap 是线程序不安全的,不是同步的 。

      3、值:只有 HashMap 可以让你将 空值 作为一个表的条目的 key 或 value 。


四。对集合的选择

      4.1、对 List 的选择

       1、对于随机查询与迭代遍历操作,数组比所有的容器都要快。所以在随机访问中一般使用 ArrayList

       2、LinkedList 使用双向链表对元素的增加和删除提供了非常好的支持,而 ArrayList 执行增加和删除元素需要进行元素位移。

       3、对于 Vector 而已,我们一般都是避免使用。

       4、将 ArrayList 当做首选,毕竟对于集合元素而已我们都是进行遍历,只有当程序的性能因为 List 的频繁插入和删除而降低时,再考虑LinkedList。


     4.2、对 Set 的选择


      1、HashSet由于使用HashCode实现,所以在某种程度上来说它的性能永远比TreeSet要好,尤其是进行增加和查找操作。

      2、虽然TreeSet没有HashSet性能好,但是由于它可以维持元素的排序,所以它还是存在用武之地的。

     4.3、对Map的选择

     1、HashMap与HashSet同样,支持快速查询。虽然HashTable速度的速度也不慢,但是在HashMap面前还是稍微慢了些,所以HashMap在查询方面可以取代HashTable。

     2、由于TreeMap需要维持内部元素的顺序,所以它通常要比HashMap和HashTable慢。


五。 案例分析 

 5.1. 将数组转化为 list

        String[] str = {"1","2","3","4","5"};

        List list = Arrays.asList(str);

        System.out.println( list );

  后台输出 :[1, 2, 3, 4, 5]


 5.2. 提取集合中的数组

       Set set = new HashSet<Integer>();
         set.add(1);
         set.add(2);
         set.add(3);
         set.add(4);

        //获取集合大小
        int size = set.size();

        // 获取同等大小的数组
        Integer[] array = new Integer[size];

        //将集合数据提取到数组
        set.toArray(array);

         System.out.println(set);
         System.out.println(array[1]);
  后台输出 :[1, 2, 3, 4]
                       2

 5.3. 遍历 map

         Map<String,String> map1=new HashMap<String,String>();

         Iterator<Map.Entry<String, String>> iter = map1.entrySet().iterator();

         while (iter.hasNext()) {
            
            Map.Entry<String, String> entry = iter.next();
            String key = entry.getKey();
            String value = entry.getValue();


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值