Day15 --框架集合 Collection集合 和 List 集合

来源:互联网 发布:国外免费代理软件 编辑:程序博客网 时间:2024/05/22 07:09
 a.
    对象数组 --自定义对象类
        概述
            * 创建一个自定义类用于存储学生信息,并且获取到每个学生信息 。
        使用
            Student[] s = new Student[5];    //创建student类,用于存储数组对象。
            s[0] = new Student("hah", 2);    //第一个Student数组的元素,且每个地址值有所不同
            s[1] = new Student("fas", 4);    // 数组中存储的元素记录的都是地址值,通过每个地址值找到一个个的new的元素对象。
            s[2] = new Student("fasd",34);
            s[3] = new Student("we", 43);
            s[4] = new Student("ad", 23);
            for (int i = 0; i < s.length; i++) {
                System.out.println(s[i]);
            } 
                     
        注意
            * 数组和集合存储 引用数据类型 的时候,存储都是地址值,且每个地址值都有所不同,通过地址值来查找每个元素对象。
            * 自定义对象数组中 存储的元素记录的其实都是地址值,即每个元素对象的地址值。 通过每个索引地址值找到每个 new的元素对象。

b.
    集合
        --由来
            * 前提:数组和集合都是容器。
            * 因为数组的长度是固定的,当添加的元素超过数组的长度需要对数组进行重新定义,这样太麻烦。 所以Java在jdk1.2的时候为我们提供了集合类。集合:用于存储任意对象,而且长度可以改变,是随着元素的增加而增加,随着元素的减少而减少的,即随着元素个数的变化而变化。
            
        --概述
            * 数组和集合都是容器。
            * 集合于jdk1.2时被提出,长度可改变,是随着元素个数的变化而变化。
        数组和集合区别
            * 可以从两方面来说:A存储的类型 和 B存储的长度:
            * 区别:
                区别1:
                   * 数组既可以存储基本数据类型,也可存储引用数据类型。基本数据类型存储的是值,而引用数据类型存储的是地址值。
                   * 集合只能存储引用数据类型(对象)集合也可以存储基本数据类型,但存储的时候会自动装箱变成对象,即int类型的100,存储到集合中就变成了new Integer(100)。
                区别2:
                    * 数组的长度是固定的,不可自动增加。
                    * 集合的长度是可变的,是根据元素个数的变化而变化。
                    
        --数组和集合什么时候使用
            * 如果元素个数固定,就用数组。
            * 如果元素个数未知,不是固定,就用集合。    --部分集合底层代码还是用数组来实现的,如果超出了数组的范围,就创建一个 大于前者的1.5倍长度的数组。增长后,将原数组变成垃圾,以此类推。
    
    
    --集合继承体系关系图        
              -Collection --单列集合根接口
                  * List -存取有序,和数组相同。有索引,可存储重复元素。
                      * ArrayList   底层 数组实现。 
                      * LinkedList  底层 链表实现。 
                      * Vector(jdk1.0) 底层 数组实现。 --已被ArrayList替代。 之所以和集合命名规则不同,是集合体系是jdk1.2时候被提出,而jdk1.0的时候已经有了Vector,因为Vector中的功能和集合一样,所以让Vector也实现了List接口并且并入到了集合体系中。
                  * Set -存取无序,无索引,不可存储重复元素。
                      * HashSet        底层 Hash算法。    元素无序。
                      * LinkedHashSet 底层 链表结构,是set集合中唯一一个怎么存怎么取的集合,又因为是HashSet的子类,所以同样保证了元素的唯一性。
                      * TreeSet        底层 二叉树算法。 元素可以进行排序。
                      
              -Map    --双列集合根接口
                   * HashMap 底层 哈希算法。 保证键的唯一性。
                   * TreeMap 底层 二叉树。 键可以排序。
                      


c.
    Collection
        --概述
            * Collection是所有 单列集合 的根类接口。
            * Map是所有 双列集合 的根接口。
            * 集合是jdk1.2的时候提出。
            * 集合的命名都有一定的规范:底层是怎么实现的,前者单词就是什么,后者是哪个继承体系下的。后者单词就是什么。
            如:
                ArrayList 底层数组实现,属于List集合下。
                LinkedList 底层链表实现,所以List集合下。
    --体系结构图:        
        Collection(单列集合根接口)
            * List(有序,有索引,可重复元素)
                * ArrayList  数组实现。存取有序,和数组一样。有索引,可存储重复元素。
                * LinkedList 链表实现。
                * Vector(jdk1.0) 数组实现。之所以和集合命名规则不同,是因为在jdk1.0时候出现,而集合体系是jdk1.2时候出现,已被ArrayList替代。
            * Set (无序,无索引,不可重复元素)
                * HastSet 哈希算法。
                * TreeSet 二叉树算法。
                
        注意:List是一个不可插队的插入对象。
        


d.
    Collection集合的基本功能
        概述
          * util包下,用时需导包。
          * Collection 是接口,用时只能:父类引用指向子类对象,即:    Collection c = new ArrayList();
          * Collection 单列集合 中的根接口。
          * Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素(list),而另一些则不允许(set)。一些collection 是有序的(list),而另一些则是无序的(set)。
          * JDK不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。
        常用方法
         * boolean add(Object obj);         添加对象 可添加任意对象。     
         *                                             结果Boolean永远都是true。那什么时候是false? 只有当set集合存储重复元素的时候,返回false。
         *                                             所以一般我们不用返回值类型,自己将对象添加到集合中即可。即:c.add("sfa");
         * 
         * boolean remove(Object obj);     删除对象 可删除任意对象。可以是基本数据类型,到时候会自动装箱成对象。
         * void clear();                     清空集合元素
         * boolean contains(Object o);        判断是否包含指定元素
         * boolean isEmpty();                判断集合是否为空,为空就返回true,否则为false。
         * int size();                        获取集合中的元素个数
         
    注意:1)add如果是List集合,就一直返回true,因为list集合中是可以存储重复元素的。
             如果是set集合用add存储重复元素,就会返回false。
         2)ArrayList的父类的父类重写了toString方法,所以在打印对象的引用时输出的结果不是Object的  toString 的结果,而是集合特有的:[元素]的形式。

         collectionXxx.java使用了未经检查或不安全的操作.
         注意:要了解详细信息,请使用 -Xlint:unchecked重新编译.
            java编译器认为该程序存在安全隐患
            温馨提示:这不是编译失败,所以先不用理会,等 学了泛型你就知道了




    toArray() 集合的遍历之集合转数组遍历的功能
        集合遍历
            * 就是依次获取集合中的每个元素。
            * 集合转数组,就可以实现对集合的遍历。
            * Object[] toArray(); 集合转数组的方式就可以遍历集合中的每个元素。
        代码
            //自定义 Bean对象 的遍历方式。
                    Collection c2 = new ArrayList(); //创建单列集合对象。
                    c2.add(new Student("哈哈",23));  //等同于 Object o = new Student("哈哈",23);
                    c2.add(new Student("嘻嘻",25));
                    c2.add(new Student("么么",332));
                    c2.add(new Student("啦啦",645));
                    
                    Object[] array2 = c2.toArray();
                    for (int i = 0; i < array2.length; i++) {
                         System.out.println(array2[i]); //获取集合中所有的元素对象。
                         
            //             如果想获取每个元素对象中的Name 或 age,就要向下转型,即Object转成Student对象。现在的array2[i] 就是Ojbect对象。
            //            (看左边的类型,如果左边是Object对象 就是向上转型,如果左边是Student对象,就是向下转型)
                         Student st =(Student)array2[i];    //向下转型。将Object的array2转成Student。
                         System.out.println("姓名:"+st.getName()+"  年龄:"+st.getAge());
                    }


    Collection集合中方法带有All后缀的功能
            * boolean addAll(Collection c);    添加集合。
                * 如:
                    * c1集合有对象:abcd  c2集合有对象:ef
                    * c1.addAll(c2); 将c2集合中的每个对象添加c1中,结果为:[a,b,c,d,e,f]
                    * c1.add(c2); 将c2看做是一个整个对象添加到c1中,结果为:[a,b,c,d,[e,f]]
                
            * boolean removeAll(Collection c);  删除集合或删除集合的元素,删除的是两个集合的交集为true,如果删除的是两个集合没有的交集,就是false。
            * boolean containsAll(Collection c); 是否一个集合包含另一个集合的所有元素。如果全包含就为true,否之为false。
            * boolean retainAll(Collection c);  取出两个集合之间的交集。
                * 注意:如果调用的集合改变就返回true, 如果调用的集合没有改变就返回false。
                     如:
                         1) c1集合有:abcd     c2集合有 ab    c1.retainAll(c2);  取交集c1结果为:ab,c1改变了,所以返回true。     
                         2) c1集合有:abcd     c2集合有 z     c1.retainAll(c2);  取交集c1结果为:[], 但c1还是改变,所以也返回true。
                         3) c1集合有:abcd     c2集合有 abcdefg     c1.retainAll(c2);   取交集c1结果为:abcd,c1没有改变,所以为false。
                     这就是retainAll的特殊之处,主要是 前者跟后者比较,前者的结果是否改变,如果改变就是true,如果没变就是false。     


    
    Collection集合的Iterator迭代器的遍历方式
            概述
                * 集合用于存储元素,存储的元素如果需要查看,就要使用迭代器的方式。
            使用
                * 使用 Iterator 的方式来迭代。
                * 关键方法:
                    * hasNext(); 判断是否还有下一个元素,如果有就返回true。
                    * next(); 返回迭代的下一个元素,即将集合中的元素取出。
                        * next具有将指针向下移一位的功能。


    Collection存储自定义对象并遍历
            Collection c = new ArrayList(); // 创建集合对象。父类引用指向子类对象
            c.add(new Student("赵白石",43));  //等等于: Object  obj = new Student("赵白石",43); 即父类引用指向子类对象,obj不能使用子类特有的属性和行为,即使用只能靠向下转型的方式才能够子类特有的属性和行为。
            c.add(new Student("沈星移",33));
            c.add(new Student("周莹",40));
            c.add(new Student("吴聘",42));
            
//        方式一:while来做
        /*    Iterator it = c.iterator();
            while(it.hasNext()){
                Student st = (Student)it.next(); //向下强转。 将it.next的Iterator的对象向下强转成Student对象。只有向下转型,才能使用子类特有的属性和行为。
                String name = st.getName(); 
                int age = st.getAge();
                System.out.println("姓名:"+name+".....年龄:"+age);
            }*/
//【注意:向下转型,才能使用子类特有的属性和行为】
            
//        方式二:for循环来做
        for (Iterator it = c.iterator(); it.hasNext();) {
                Student s = (Student)it.next();  //向下强转。将it.next的Iterator的对象向下强转成Student对象。这样就能获取子类特有的属性和行为。
                System.out.println(s.getName()+"..."+s.getAge());
        }
    }

    --迭代器的原理及源码解析
         原理    
             迭代器是对集合进行遍历的,而每个集合内部的存储结构都是不同,所以每个集合的存和取的方式也都不同的。那么就需要在每一个类中都去定义hasNext()方法 和 next()方法,这样做可以方便的判断是否有下一个元素和返回集合中的当前元素。但是这样整个集合体系看起来就会过于臃肿。所以就将迭代器向上抽取出一个接口,然后在每个类的内部去定义自己的迭代方式。 这样做的好处有二:
             第一:规定了整个集合体系的遍历方式通过通过hasNext()和next()方法来实现的。第二:代码由底层内部实现,使用者无需关心怎么实现的,会用即可。

        迭代器的原理:
        迭代器是用来遍历集合。因为集合的底层数据结构不同,就导致集合存取元素的方式不同,如果每个集合类都定义自己的存取方式,这样做是可以的,但是会让整个继承体系很冗余,臃肿。所以我们经过不断的向上抽取,最终定义成了接口。
        这样做的好处有两个:
            A:更规范。
            B:我们不用管底层是怎么实现,只要会用就可以了。
        
        
        迭代器源码解析
              * 1,在eclipse中ctrl + shift + t找到ArrayList类
              * 2,ctrl+o查找iterator()方法
              * 3,查看返回值类型是new Itr(),说明Itr这个类实现Iterator接口
              * 4,查找Itr这个内部类,发现重写了Iterator中的所有抽象方法 
            
        Iterator iterator(); 该对象必须依赖具体的集合容器,因为每个集合容器的内部数据结构不同,所以该迭代器对象是在集合容器中进行内部实现的,也就是说iterator()方法在每个集合容器中的实现方式时不同的。
        对于调用者来说,具体的实现过程无需知道,只要通过该集合容器获取到该实现的迭代器对象即可,也就是iterator方法。
        Iterator接口就是对所有单列集合根接口Collection容器进行元素取出的公共接口。    


e.
    List集合 
        概述
            *  List是一个接口,在util包下,使用的话,只能父类引用指向子类对象,如下:
                    * List i = new ArrayList(); 但这样做存在弊端,不能使用子类特有的属性和方法。
                    * 建议:ArrayList al = new ArrayList();    // 这样写。直接new对象即可直接使用子类属性和方法。
                    * 因为多态当 [参数] 用是比较合适。因为可以更好的扩展代码功能。
            * 有序集合,有索引,可存储重复元素。
            * ArrayList底层 数组实现。
            
        方法
            * void add(int index, E element); 在集合的指定索引位置上添加E对象。E表示泛型表示任意对象。
                * 注意:
                    * 当添加时使用不存在的索引,就会出现:IndexOutOfBoundsException 角标越界异常。
                    * 添加的索引是: index<=size 并且index>=0 在这个范围内 就都不会出错报异常。
            * E remove(int index); 通过集合的索引删除元素,将被删除的元素返回。
            *     *  remove的过程不会将索引自动装箱成对象。
            * E get(int index);  获取集合指定索引上的元素对象。
                * get(int index)方法可作为for循环变量集合的其中一个条件,配合size()方法便可遍历出具体集合元素对象。         
            * E set(int index, E element); 设置集合索引上的元素。
            * int indexOf(Object o); 返回此列表中第一次出现指定元素的索引位置,如果没有就返回-1
            * List subList(int fromIndex. int toIndex);    返回列表中从指定位置开始到指定位置结束之间的部分视图。含头不含尾的。
            

    List集合的遍历
        概述
            * 通过get() 和 size()方法结合使用遍历。
            * 代码
                List list = new ArrayList(); //创建集合对象。
                list.add(new Student("aa",33)); //添加Student元素对象,等于 Ojbect o = new  Student("aa",33)
                list.add(new Student("bb",23));
                list.add(new Student("cc",13));
                //遍历集合,使用size()和get()方法去遍历。
                for(int i=0;i<list.size();i++){
                    Object obj = list.get(i); //获取每个集合元素对象。
                    sop(obj);
                    //如果想获取具体Student对象的name和age属性,就要向下强转对象,再获取。因为父类不能使用子类特有的属性和行为。
                    Student st = (Student)obj; //向下强转对象。
                    sop(st.getName()+"..."+st.getAge());
                }



    List集合 --并发修改异常产生的原因及解决方案
        * 需求
            * 我有一个集合,请问,我想判断里面有没有"world"这个元素,如果有,我就添加一个"javaee"元素,请写代码实现。
        
        * 问题出现
            * 如果使用Iterator普通迭代器 来遍历集合且同时添加元素对象时,就会出现 并发修改异常,即ConCurrentModificationException。
            * 当使用Iterator普通迭代器 来作为遍历的方式时,一边遍历元素对象并且一边添加元素对象,就会出现这样的异常。
        
        * 解决方案
            * 使用List集合特有的一种迭代方式:ListIterator。
            * ListIterator迭代器可以 一边遍历集合,一边使用ListIterator迭代器可以特有的add方法添加元素(不是ArrayList的add方法),这样才不会有 并发修改异常 的出现。
    
        
     List集合中特有的迭代器 ListIterator 的了解:
        * boolean hasNext(); 正向顺序判断是否还有下一个元素。
        * boolean hasPrevious(); 逆向顺序判断是否还有前一个元素。
        * Object next(); 正向顺序返回下一个元素。
            * next()获取元素并将指针向后移动一次。     
        * Object previous(); 逆向顺序返回上一个元素。            
            * previous()获取元素并将指针向前移动一次     
    注意:必须先有正向遍历,才可以逆向遍历。    
    



    Vector(1.0)
        概述
            * 有序集合,在util包下,是一个类。
            * 在jdk1.2出现后的集合体系将Vector并入里面,且用 ArrayList代替这个集合了。
            * 底层 数组实现。
            * 同样具有 Iterator 迭代器,但该迭代器不是自己的,而是从父类接口List继承下来的。
            * 他有自己遍历集合的方式:Enumeration
        
     特有功能
            * void addElement(E obj); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 add() 去添加元素。
            * E elementAt(int index); 在没有并入集合系统时,使用的是这个方法添加元素,当并入以后就是用 nextElement() 去添加元素。
            * Enumeration elements(); 获取枚举 等同于 Iterator迭代器。
            




【遍历集合的几种方式】
list
    * 使用 toArray() 集合转数组的方式 + 普通for循环。
    * 使用 List中的 size() 和 get(int index) 的方式for循环获取集合元素。
    * 使用 Iterator迭代器, 直接遍历集合获取元素,如果普通迭代器 一边遍历集合元素,一边添加元素,这样会一定出现:并发修改异常:ConcurrentModificationException。
    * 使用 List集合中特有的:ListIterator迭代器,一边遍历集合元素,一边使用ListIterato特有的add()方法添加(修改)元素的方式,这样就不会出现 并发修改异常。

vector
    * Iterator迭代器
    * Vector vec = new Vector(); Enumeration ele = vec.elements(); 获取枚举的方式。




f.
    数组结构之数组和链表 2017/10/5 0:24:53 
        概述
            * 底层:数组 --ArrayList,Vector
                * 查询快,修改快,但增删慢。(这些都是相对而言的)
                
            * 底层:链表 --LinkedList
                * 查询慢,修改慢,但增删快。
解析:
    --集合中的 底层:数组结构 是怎样的一个流程??
            * 因为部分集合底层结构是由 数组 来实现的,所以会在底层创建一个数组为10的初始长度,如果要存储的数据超过了这个数组初始长度时即11个时,会再创建一个比原来大50%的数组长度,将原数组中的数据copy到这个新数组当中,将原数组扔掉...以此类推。 因为数组一旦创建,长度就不能被改变,所以随着存储数据的个数越多,数组的长度不够用,就要创建更多的新数组,这样会导致废弃的旧数组越来越多成为垃圾,而占用内存空间。
                * 底层结构:数组
                    * 什么是数组?就是一个容器,里面规定了初始容量是多少的一个器皿。比如初始容量就是一个可以存储10个元素的容器,这就是数组,数组一旦创建就不可被修改。
                    
                        * 查询快,修改快。是因为数组有索引,根据索引可以方便的 查找 和 修改 对应的数据。
                        
                        * 增删慢。因为都要对索引对应的数据进行操作,所以慢,如下:。
                            * 增加慢,如:在索引2的位置上添加一个元素E,就要将索引2包含2的元素整体往后移动一位。将索引2的元素置出,并放入新添加的E元素。
                            * 删除慢,如:将索引3的元素删除,就是将3+1的索引位置开始往后的元素往前移动一位,移动的最末尾的那个空出来的索引上的元素置为null即可。
                            
    --集合中的 底层:链表结构 是怎样的一个流程??                        
                    * 底层结构:链表
                        * 什么是链表? 比如就是一段段自行车的小链条。 他们的链接就是通过 中间链条记录住前面链条的地址值,和再记录住后面链条的地址值,通过这样的形式将链串联起来,就是链表。
                        * 前提?链表不像数组是一个容器有规矩的索引编号,增和删 索引都要变化。而链表只是通过记录前后地址值来排列索引而记录数据。
                            * 增加快。所以如果想添加一个元素,可在任意的链条中间添加即可,但增加的同样要记录住它前后的元素的地址值,从而形成一个新的链表。
                            * 删除快。如果想删除一个元素,直接删除,删除后的其他两个的前后重新拼接一个新的中间链条即可。
            
                            * 查询慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,这样对比数组直接通过拿索引找元素肯定会慢些。
                            * 修改慢:因为要先判断要查找的元素距离开头近还是末尾近,确定后。再通过Next(下一个)还是previous(前一个)的方式查询,查到后再修改。这样对比数组直接通过拿索引找到元素并修改它肯定会慢些。
                            
                            



List集合的三个子类的特点
        概述        
            * ArrayList
                * 底层数据结构:数组。因为是数组,所以:查询快,修改快。但增删慢。
                * 它是 线程不安全 的,不同步。所以操作速度快,效率高。(最后通过StringBuilder来记录元素)
            * Vector
                * 底层数据结构:数组。因为是数组,所以:CURD都慢。
                * 它是 线程安全 的,同步。所以操作速度慢,效率低。
            * LinkedList
                * 底层数据结构:链表,因为是链表,所以:查询慢,修改慢,但增删快。
                * 它是 线程不安全,不同步,所以执行效率高。

            Vector要比ArrayList查询慢,因为Vector是线程安全的。
            Vector要比LinkedList增删慢,因为Vectory是数组结构的,LinkedList是链表结构的,链表结构增删快,数组增删慢。

Vector和ArrayList的区别
            * Vector是 线程安全,所以执行效率低。
            * ArrayList是 线程不安全的,所以执行效率高。
            * 共同点:底层都是 数组 实现的。    
    
ArrayList和LinkedList的区别 
            * ArrayList底层是 数组结构,所以查询快,修改快,增删慢。
            * LinkedList底层是 链表结构,所以查询慢,修改慢,增删快。

        单列集合Collection下的List有三个子类,我们到底使用谁?
            * 查改多,用ArrayList
            * 增删多,用LinkedList
            * 如果都多,就用ArrayList。

    

【线程安全,意味着有 锁。】






【面试题】    

​    1、集合的继承体系结构?
    总:
        集合体系有两大部分: 单列集合Collection  双列集合Map
    分:
        单列结构Collection 下有 List 和 Set
            List 有序 有索引,可存储重复元素。
                * List下有三个子类
                    * ArrayList
                        * 底层结构:数组。查询快,修改快,增删慢。
                        * 线程不安全的,执行效率高。
                    * LinkedList
                        * 底层结构:链表。查询慢,修改慢,增删快。
                        * 线程不安全的,执行效率高。
                    * Vector
                        * 底层结构:数组,查询快,修改快,增删慢。
                        * 线程安全的,执行效率低。
            Set  无序,无索引,不可存储重复元素。


    总:


​    2、数组和集合区别?
        数组,既可以存储基本数据类型,也可存储引用数据类型,当存储基本数据类型存储的是值,存储引用数据类型时存储的是地址值。
        而集合,只能存储引用数据类型(对象),当集合存储基本数据类型时,会自动装箱封装为对象。(如:int变成Integer对象, char变成Character对象,boolean变Boolean对象)
        
        数组的长度是固定的,一旦初始化不可更改,不可增长。
        集合的长度是可变的,可以根据元素的增长而增长,随着元素的减少而减少 --(由元素个数决定)。

​    3、如何获取字符串、数组、集合的长度?
            * 字符串: "".length();
            * 数  组: Length属性;
            * 集  合: size()方法;



   4、遍历集合有哪几种方式?
        List
            1) 使用 toArray() 将集合转数组的方式 + 普通for循环。
            2) 使用 List中size()和get(int index)的方法for循环获取集合元素。
            3) 使用 Iterator迭代器,直接遍历集合获取元素。如果使用普通迭代器Iterator 来一边遍历集合元素,一边添加集合元素。一定会出现:并发修改异常。即:ConcurrentModificationException
            4) 使用 List中特有的ListIterator 迭代器,就可以一边遍历集合元素,一边使用ListIterator中特有的add方法添加元素,这样就不会有 并发修改异常 的出现。 
          
        Vector
            1) 使用 Iterator迭代器、
            2) Vector vec = new Vector(); Enumeration e = evc.elements(); 获取枚举的方式。

   5、什么时候用数组?什么时候用集合?
        查改多,用ArrayList
        增删多,用LinkedList
        如果都多,用ArrayList


   6、来说说你对 ArrayList、LinkedList、Vector的理解?
原创粉丝点击