美文网首页
Collection集合、Collections工具类

Collection集合、Collections工具类

作者: vv_64ce | 来源:发表于2020-04-14 01:23 被阅读0次

    集合框架概述

    1.集合、数组都是对多个数据进行存储操作的结构,简称java容器
    此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg数据库中)

    2.数组在存储多个数据方面

    (1)特点:一旦初始化后,长度确定;元素数据类型确定,只能操作指定类型的数据;如:String[ ]、Object[ ] arr2
    (2)缺点:
    a.一旦初始化,长度不可修改;
    b.数组提供的方法有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高;
    c.获取数组中实际元素个数的需求,数组没有现成的属性和方法;
    d.数组有序、可重复,对于无序、不可重复需求,不能满足

    java集合分为Collection和Map两种体系

    Collection接口:单列数据,定义存取一个一个对象的方法集合
    1. List接口:有序,可重复(动态数组)

    |--------------------ArrayList:作为List的主要实现类,线程不安全,效率高;底层使用Object[ ]存储、
    |--------------------LinkedList:对于频繁的插入、删除操作,其比ArrayList高;底层使用“双向链表”存储
    |--------------------Vector:作为List接口的古老实现类,线程安全,效率低;底层使用Object[ ]存储

    2.Set接口:无序,不可重复(数学中的“集合”),操作过滤问题,即相同数据过滤掉

    |--------------------HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
    |-------------------------------LinkedHashSet:在HashSet基础上添加了Link,是HashSet的子类,使其在遍历内部数据时,可以按照添加的顺序遍历(不同于有序)
    |--------------------TreeSet:可以按照添加对象的指定属性进行排序


    Collection接口
    Map:双列数据,保存具有映射关系”key-value对“的集合(数学中函数y=f(x))

    |------HashMap:Map的主要实现类,线程不安全,效率高;可以存储null(jdk7之前,底层数组+链表;jdk8,数组+链表+红黑树)
    |--------------------LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有HashMap的底层结构基础上,添加了一堆指针,指向前一个和后一个元素,对于频繁的遍历操作,执行效率高于HashMap
    |------TreeMap:按照添加的key-value对进行排序,实现排序遍历。其中key的排序为自然排序或定制排序;底层使用的红黑树
    |------Hashtable:古老的实现类,线程安全,效率低;不能存储null
    |--------------------Properties:常用来处理配置文件,其中key和value都是String


    Map接口
    Collection接口中声明方法的测试

    向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()

            Collection c1 = new ArrayList();
            c1.add(new Person("xiaohong",12));
            c1.add(new Person("xiaohong",12));
            //false,未重写equals方法时:即Object的equals方法相当于==,基本数据类型,比较值;引用数据,比较地址
    //        System.out.println(c1.contains(new Person("xiaohong", 12)));
            System.out.println(c1.contains(new Person("xiaohong", 12)));//true,重写equals方法时,即比较值
            Collection c2 = Arrays.asList(12);
            System.out.println(c1.containsAll(c2));
    
    

    下列方法操作的对象:

            Collection c1 = new ArrayList();
            c1.add(12);//表示数字
            c1.add("12");//表示字符串
            c1.add(new Person("xiaohong",12));
            c1.add(new Person("xiaohong",12));
    
            Collection c2 = Arrays.asList(12,13);
    

    (1)retainAll(Colection coll):交集

    System.out.println(c1.retainAll(c2));//true,两个集合的交集
    System.out.println(c1);//[12]  交集后输出c1
    
    

    (2)removeAll(Collection coll):差集

     System.out.println(c1.removeAll(c2));//true,移除c1中存在的c2,即差集
     System.out.println(c1);//[12, com.vv.string.Person@0, com.vv.string.Person@0]
    

    (3)hashCode():哈希值

     //返回当前对象的哈希值
            System.out.println(c1.hashCode());
    

    (4)toArray():集合--》数组

            //1.集合---->数组
            Object[] objects = c1.toArray();
            for (int i = 0; i < objects.length; i++) {
                System.out.print(objects[i] + "\t");
            }
            System.out.println();
    
            //2.数组----->集合
            List<Object> objects1 = Arrays.asList(objects);
            System.out.println(objects1);//[12, 12, com.vv.string.Person@0, false]
    
            //a.基本数据类型数组--->集合  将数组整体存入
            List<int[]> ints = Arrays.asList(new int[]{123, 145});
            System.out.println(ints);//[[I@5d6f64b1] int型的一维数组
            System.out.println(ints.size());//1
    
            //b.包装类的数组--->集合
            List<Integer> integers = Arrays.asList(new Integer[]{12, 45});
            System.out.println(integers);//[12, 45]
            System.out.println(integers.size());//2
    
    
    (5)Iterator(迭代器)

    1.主要用于遍历Collection集合中的元素;
    2.Collection接口继承了java.lang.Iterable接口,该接口有一个iterator方法,因此所有实现了Collection接口的集合都有一个iterator方法,用来返回一个实现了iterator接口的对象
    3.Iterator仅用于遍历集合,其本身并不提供承装对象的能力;但若需要创建Iterator对象,则必须有一个被迭代的集合
    4.每次调用iterator方法都会得到一个全新的迭代器对象


    迭代器执行原理

    两种错误的迭代方式:

     // 错误方式一:结果跳着输出且NoSuchElementException
            Iterator iterator = c1.iterator();
            while(iterator.next() != null){
                System.out.println(iterator.next());//
            }
    
    //每次c1.iterator().hasNext()都会创建新的迭代器对象
    //错误方式二:一直输出第一个位置的值
            while (c1.iterator().hasNext()){
                System.out.println(c1.iterator().next());
            }
    

    (6)remove(Object obj)

    Iterator iterator = c1.iterator();
            while (iterator.hasNext()){
               if("13".equals(iterator.next())){//????数字如何和迭代器元素比较
                    iterator.remove();
                }
    //            System.out.print(iterator.next()+"\t");//出错,NoSuchElementException,原因是迭代器中元素改变了
            }
            Iterator iterator1 = c1.iterator();
                while (iterator1.hasNext()){
                System.out.print(iterator1.next()+"\t");//12    Person{name='xiaohong', age=14} false
            }
    
    length、length()、size()区别

    length——数组的属性;
    length()——String的方法;
    size()——集合的方法;

    JDK5.0:ForEach遍历集合和数组

    //for(集合元素类型 局部变量:集合对象)
    for(Object obj:coll)

    Iterator iterator1 = c1.iterator();
            for(Object c : c1){//内部仍然调用了迭代器
                System.out.print(iterator1.next()+"\t");
            }
    

    例题

    int[] arr = new int[]{112,3,4};
            //方式一赋值
            for (int i = 0; i < arr.length; i++) {
                arr[i] = 4;//对本身的数组元素进行修改
            }
    
            //方式二赋值
    //        for(int a : arr){
    //            a = 4;//取出来,重新对s赋值。在常量池中
    //        }
    
            for(int a : arr){
                System.out.print(a + "\t");//方式一:4  4   4  方式二:112  3   4
            }
    

    List接口

    ArrayList、LinkedList、Vector三者异同
    ArrayList

    jdk7.0中的ArrayList对象的创建类似于单例的饿汉式,底层创建了长度为10的Object[ ];而jdk8.0中的ArrayList对象的创建类似于单例的懒汉式,底层的Object[ ]初始化为{ },延迟了数组创建,节省内存
    扩容2倍

    LinkedList

    内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。其中Node用于保存数据,其prev和next两个变量分别记录前一个和后一个元素
    扩容1.5倍

    List接口中常用方法

    List中:remove(int index)

            ArrayList list = new ArrayList();
            list.add(12);
            list.add("xiaohong");
            list.add(new Person("hh",10));
            list.add(false);
    
            System.out.println(list.remove(2));//Person{name='hh', age=10}
    

    区别Colection中:remove(Object o)

            c1.add("20");//表示字符串
            c1.add(new Person("xiaohong",12));
    
            System.out.println(c1.remove("20"));//true
            System.out.println(c1);//[Person{name='xiaohong', age=12}]
    

    subList(begin end)类似于String中的subString(begin end)都是左开右闭

    List中主要方法

    增/插add删remove改set查get长度size()遍历(1)Iterator(2)forEach(3)普通循环
    例题:remove考察

    @Test
        public void test1(){
            List list = new ArrayList();
            list.add(1);
            list.add(2);
            list.add(3);
    
            updateList(list);
            System.out.println(list);
    
        }
        void updateList(List list){
    //        list.remove(2);//[1 ,2] 删除索引位置的值
            list.remove(new Integer(2));//[1,3]  删除该对象
        }
    

    Set接口中的方法

    Set接口中没有额外定义的新方法,使用的都是Collection中声明过的方法(无序即代表没有索引)
    List接口中有额外定义的新方法,因为List中有索引(有顺序即代表有索引)

    HashSet论证Set的无序性,并不是代表随机性

    HashSet存储数据时,底层数组并非按照数组索引的顺序添加,而是根据数组的哈希值确定
    不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个

     @Test
        public void test(){
            Set set = new HashSet();
            set.add(12);
            set.add("hh");
            set.add(new Person("zhangsan",10));
            set.add(new Person("zhangsan",10));
    
            System.out.println(set);
        }
    

    添加元素:以HashSet(其底层是数组+链表)为例:
    向HashSet中添加元素a,先调用hashCode,计算其哈希值,然后哈希值接着通过某种计算方法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:若没有,则a添加成功;若有元素b,首先比较a,b的哈希值,若哈希值不相同,则a添加成功;若相同,进而需要调用元素a所在类的equals方法,equals返回true,a添加失败。返回false,a添加成功
    对于a添加时其位置有元素而言,a和已经存在于指定索引位置上的数据之间以链表方式存储(jdk7,a放到数组中,指向原来的元素;jdk8,原来的元素在数组中,指向元素a)


    HashSet底层结构
    Set set = new HashSet();
            set.add(12);
            set.add("hh");
            set.add(new Person("zhangsan",10));
            set.add(new Person("zhangsan",10));
    //        //当未重写equals和hasCode时
    //        System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
    
    //        //当只重写equals时
    //        System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
    
            //当只重写hasCode时
            System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12, Person{name='zhangsan', age=10}]
    
    //        //当重写equals和hasCode时
    //        System.out.println(set);//[hh, Person{name='zhangsan', age=10}, 12]
    
    向Set中添加的数据,其所在的类必须进行hashCode和equals重写,重写的hashCode和equals尽可能保持一致性,相等的对象必须具有相等的散列码(hashCode)
    LinkedHashSet底层结构
    LinkedHashSet

    作为HashSet的子类,在添加数据时,每个数组还维护了两个引用,记录此数据的前后数据,其对于“频繁的遍历操作”,LinkedHashSet频率高于HashSet


    LinkedHashSet
    TreeSet

    1.向TreeSet添加数据,必须是相同的对象;
    2.排序方式自然排序、定制排序

    自然排序:

    两对象进行比较时,为CompareTo方法,不再是equals方法

    Set set = new TreeSet();
    /*
            TreeSet中存放不同对象出错java.lang.Integer cannot be cast to java.lang.String
    
            set.add(12);
            set.add("hh");
            set.add(new Person("xiao",15));
            System.out.println(set);
    */
            /*
            //1.基本数据类型存储,默认从小到大排序
            set.add(12);
            set.add(32);
            set.add(2);
            System.out.println(set);//[2, 12, 32]
            */
            //2.对象存储,
            /*
             未对对象实现Comparable,重写CompareTo方法,即并未告知TreeSet以对象的哪种属性进行排序
             com.vv.string.Person cannot be cast to java.lang.Comparable
            */
    
            set.add(new Person("hong",15));
            set.add(new Person("wang",5));
            set.add(new Person("zhang",35));
            set.add(new Person("zhang",25));
    
            /*在Person中实现Comparable接口时,对属性进行按照指定大小进行排序*/
    //        //当仅对name属性进行比较时
    //        System.out.println(set);//[Person{name='hong', age=15}, Person{name='wang', age=5}, Person{name='zhang', age=35}]
    
            //当对name和age属性进行比较时
            System.out.println(set);//[Person{name='hong', age=15}, Person{name='wang', age=5}, Person{name='zhang', age=25}, Person{name='zhang', age=35}]
    

    定制排序:Comparator

    Set set = new TreeSet(new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    if(o1 instanceof  Person && o2 instanceof Person){
                        return -Integer.compare(((Person) o1).age,((Person) o2).age);
                        //[Person{name='zhang', age=35}, Person{name='zhang', age=25}, Person{name='hong', age=15}, Person{name='wang', age=5}]
                    }else{
                        throw new RuntimeException("对象类型不一致");
                    }
    
                }
            });
    

    例题:
    1.属性排序

    //生日排序
     @Test
        public void test(){
            Employee e1 = new Employee("xiaohong",12,new MyDate(1926,2,6));
            Employee e2 = new Employee("zhangsan",82,new MyDate(1823,3,7));
            Employee e3 = new Employee("lisi",62,new MyDate(1926,5,6));
            Employee e4 = new Employee("zhaoliu",12,new MyDate(1753,2,9));
            Employee e5 = new Employee("dawei",18,new MyDate(1926,5,3));
    
            //使用Comparator對生日進行排序
            TreeSet set = new TreeSet(new Comparator() {
                @Override
            public int compare(Object o1, Object o2) {
    
                if (o1 instanceof Employee && o2 instanceof Employee) {
                    Employee e1 = (Employee) o1;
                    Employee e2 = (Employee) o2;
    
                    MyDate m1 =  e1.getBirthday();
                    MyDate m2 = e2.getBirthday();
    
                    /*//方式一:直接在该方法中比较
                    int year = m1.getYear() - m2.getYear();
                    //比较年
                    if (year != 0) {
                        return Integer.compare(m1.getYear(), m2.getYear());
                    }
                    int month = m1.getMonth() - m2.getMonth();
                    //比较月
                    if (month != 0) {
                        return Integer.compare(m1.getMonth(), m2.getMonth());
                    }
                    //比较日
                        return Integer.compare(m1.getDay(), m2.getDay());*/
    
                    //方式二:在MyDate中实现Comparable接口
                    return m1.compareTo(m2);
                }
    
                throw new RuntimeException("對象不一致");
            }
    
        });
    //        TreeSet set = new TreeSet();
            set.add(e1);
            set.add(e2);
            set.add(e3);
            set.add(e4);
            set.add(e5);
    
    //        Iterator iterator = set.iterator();
            for(Object o : set ){
                System.out.println(o);
    
            }
    
        }
    
    //name排序的同时,按照生日排序
    //在Person类中比较
    @Override
        public int compareTo(Object o) {
            if(o instanceof  Employee){
                Employee e = (Employee) o;
    
                int i =this.name.compareTo(e.getName());
                if(i == 0){
                    return this.getBirthday().compareTo(e.getBirthday());
                }else{
                    return i;
                }
    }
            throw new RuntimeException("对象类型不一致");
        }
    

    2.在List内去除重复元素

     public static void main(String[] args) {
            List list = new ArrayList();
    
            list.add(new Integer(2));
            list.add(new Integer(2));
            list.add(new Integer(1));
            List list1 = updateSet(list);
    
            for(Object o : list1){
                System.out.print(o + "\t");
            }
    //        for (int i = 0; i < list.size(); i++) {
    //            System.out.println(list.get(i));
    //        }
    
        }
        public static List updateSet(List list){
            HashSet set = new HashSet();
            //若list中添加的是自定义对象,则需要进行hashCode重写;Integer的equals方法已经重写了
            set.addAll(list);
            return new ArrayList(set);
        }
    

    3.set***********太坑了

    HashSet set = new HashSet();
            Person p1 = new Person("AA",11);
            Person p2 = new Person("BB",22);
    
            set.add(p1);
            set.add(p2);
    
            p1.setName("CC");
            set.remove(p1);
    
            // 首先算出此时name为CC和age为11的哈希值,再与name为AA和age为11的哈希值比较,结果不等;
            // 因此,此时删除的位置其实为空,故存储结果为[Person{name='BB', age=22}, Person{name='CC', age=11}],
            System.out.println(set);
    
            set.add(new Person("CC",11));
            //name为CC和age为11的位置存储添加的数据,其中结果的值,一个为AA所在位置的值替换,另一个为新的位置
            // [Person{name='BB', age=22}, Person{name='CC', age=11}, Person{name='CC', age=11}]
            System.out.println(set);
    
            //虽然AA的位置仍在,但通过equals比较时发现,两者对象不一样,因此使用链表重新指向
            //[Person{name='BB', age=22}, Person{name='CC', age=11}, Person{name='CC', age=11}, Person{name='AA', age=11}]
            set.add(new Person("AA",11));
            System.out.println(set);
    

    Map接口

    Map中的key:无序、不可重复,使用Set存储所有的key--》key所在类要重写equals和hashCode方法(例如:containsKey需要重写equals和hashCode)
    Map中的value:无序、可重复,使用Collection存储所有的value--》value所在类要重写equals方法(例如:containsValue需要重写equals方法)
    一个键值对:key-value构成了一个Entry对象。
    Map中的entry:无序、不可重复,使用Set存储所有的entry


    jdk7的HashMap底层原理
    jdk8的HashMap底层原理

    map元试图操作方法:

     Map map = new HashMap();
            map.put(1,2);
            map.put(null,3);
            map.put("AA",9);
    
            //遍历key
            Set set = map.keySet();
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                System.out.println(iterator.next());
            }
    
            System.out.println();
            //遍历value
            Collection values = map.values();
            Iterator iterator1 = values.iterator();
            while (iterator1.hasNext()){
                System.out.println(iterator1.next());
            }
    
            //遍历key-value
            //方式一:
            Set entrySet = map.entrySet();
            Iterator iterator2 = entrySet.iterator();
            while (iterator2.hasNext()){
                Object obj = iterator2.next();
                Map.Entry entry = (Map.Entry) obj;
                System.out.println(entry.getKey()+"......"+entry.getValue());
            }
    
            //方式二:
            Set set1 = map.keySet();
            Iterator iterator3 = set1.iterator();
            while (iterator3.hasNext()){
                Object obj = iterator3.next();
                Object value = map.get(obj);
                System.out.println(obj+"......"+value);
            }
    

    TreeMap的key必须是同一个类创建的对象,因此要按照key排序:自然排序或者定制排序

            Map map = new TreeMap();
            map.put(new User("zhangsan",16),"shanxi");
            map.put(new User("lisan",16),"xian");
            map.put(new User("lisan",15),"dali");
    
            Set set = map.entrySet();
            Iterator iterator = set.iterator();
            while(iterator.hasNext()){
                Object obj = iterator.next();
                Map.Entry entry = (Map.Entry) obj;
                System.out.println(entry.getKey()+"..........."+entry.getValue());
            }
    

    Collections是一个操作Collection(Set、List)和Map等集合的工具类

    注意:copy方法

     List list = new ArrayList();
            list.add(12);
            list.add(34);
            list.add(34);
            list.add(85);
    
    //        //错误写法一:Source does not fit in dest,看源码后:list.size() > list1.size()
    //        List list1 = new ArrayList();
    //        Collections.copy(list1,list);
    
              //错误写法二:list1.size()为0,size()表示add后的数组长度
    //        List list1 = new ArrayList(list.size());//表示的是数组的长度
    //        Collections.copy(list1,list);
    
            //eg:
            List l = new ArrayList(9);//创建长度为9的数组
            System.out.println(l.size());//0
    
            List dest = Arrays.asList(new Object[list.size()]);
            Collections.copy(dest,list);
            System.out.println(dest);//[12, 34, 34, 85]
    

    Collections中的synchronizedXxx()方法,可使指定集合包装成线程同步的集合,从而解决多线程并发问题

    相关文章

      网友评论

          本文标题:Collection集合、Collections工具类

          本文链接:https://www.haomeiwen.com/subject/dfzwmhtx.html