Collection集合、Collections工具类

2020-04-14  本文已影响0人  vv_64ce

集合框架概述

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()方法,可使指定集合包装成线程同步的集合,从而解决多线程并发问题

上一篇下一篇

猜你喜欢

热点阅读