Java中Collection和Map的区别和联系——主要是概念性和理解性的东西

java中的几个集合框架的设计思想和底层逻辑始终容易忘,而且容易分不清楚
这里自己写一下做个整合,主要是概念性的东西,具体代码和各自的方法没有描述。

Collection

首先是Collection接口,Collection接口下有两个子接口,一个是List,一个是Set。Collection接口中有方法iterator,这个就是遍历器,只有collection的实现类才有,包括子接口的实现类。强化后的foreach其实也是基于这个,也就是说强化型for也只能用于Collection的实现类。
而Map明明不是Collection的子接口,为什么其实现类也可以用iterator?这个在下面的Map里细讲。

List

List接口主要是实现有序的,这里何为有序?不是说按照某一个值从小到大排列的有序,而指的是存放地址是有序的,也就是说可以类似于数组一样,根据下标去取值,这样很快,当然取值的代码并不是list[1]这种,而是list.get(1);这种形式,但是并不影响它的取值速度很快。

ArrayList

而List的接口的主要实现类就是我们的ArrayList。顾名思义,ArrayList就和Array有点类似。而Arrays是一个工具类,这个工具类中就包含了将Array转换为List的方法,即Arrays.asList(Object[] obj);

Set

Set和List不太一样,Set不允许有重复的数据!不论是HashSet实现类也好,还是TreeSet实现类也要,具体怎么办到的。可以看下文

HashSet

Set,其主要的实现类就是HashSet。其跟List不一样,不是有序的(注意这里的有序也指的是存放地址的连续性),因此,其一旦要查找集合中的某个值,就要从头到尾遍历然后equals?肯定不行,这样太慢了,那么原理就是Hash,所以才取名叫HashSet,存放的具体形式呢就是先根据要查找的值计算Hash值,定位在数组中的位置,然后根据数组中存放的地址去查找一个链表,而这个链表就是每一个Hash值都相同的才放在这个链表中的。那么如何做到Set中不允许有相同的数据呢?和查找原理类似,先比较HashCode,一样之后再比较equals方法,还是一样,那不就是一样了吗?所以不让放进去,或者说放进去了也是覆盖!!所以Set的主要实现类也叫HashSet。这是关于HashSet比较有意思的题

@Test
    public void test(){
        HashSet hashSet = new HashSet();
        Person p1 = new Person("AA", 13);
        Person p2 = new Person("BB", 12);

        hashSet.add(p1);
        hashSet.add(p2);
        System.out.println(hashSet);

        p1.setName("CC");
        hashSet.remove(p1);
        System.out.println(hashSet);
        /**
         *
         * 这个很有意思,就是要深入理解hashset的原理,因为hashset在add p1的时候
         * 通过hashcode放的位置加入是3位置,但是当怕p1更改了他的name值之后
         * 再想移除掉p1的时候,会用hashcode去计算新的p1对应的哈希值,最后找到的位置比如说是4
         * 那么就自然而然,会产生4没有数据的问题,那就不会删除。
         */

        hashSet.add(new Person("CC", 13));
        System.out.println(hashSet);
        /**
         * 这里还是放进去了,原理与上面一样,p1 放的哈希值是name为AA的时候放的,而这里计算的哈希值
         * 是以name为CC算的,所以两个放的位置不一样,自然就不会用equals去对比
         */

        hashSet.add(new Person("AA", 13));
        System.out.println(hashSet);

        /**
         * 这里还是进去了,很有意思,原理吗,其实也差不多,因为这里计算的哈希值其实是和p1一样的
         * 但是通过equals对比的时候,发现name不一样,因为p1放进去后改了name,所以不一样,就还是可以放进去
         */
    }

TreeSet

这里本质上就是排序二叉树,那么既然是排序二叉树,自然要排序的,这个跟上面的有序就不一样,这里是根据每个元素的某一特定值去判断值的大小,然后从小到大,或者从大到小排序,所以这里的有序和List的有序完全不是一个概念。这里的有序是指值有序,而List的有序是指连续存取地址有序。
那么这里又涉及到一点小疑惑,比如当我一直放的都是对象的时候,那么根据对象的内的某一个值进行对比,当然是可以的。但我之所以要用集合框架而不是数组,不就是因为我存放的数据类型都不尽相同吗

@Test
    public void test3(){
        Comparator comparator = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof User && o2 instanceof User) {
                    User u1 = (User) o1;
                    User u2 = (User) o2;
                    int value = u1.getName().compareTo(u2.getName());
                    if (value != 0) {
                        return value;
                    }
                    return -(u1.getAge() - u2.getAge());
                }
                throw new RuntimeException("类型不匹配");
            }
        };

        TreeSet set = new TreeSet(comparator);
        User u1 = new User("Tom",11 );
        User u2 = new User("Jerry",21 );
        User u3 = new User("Rose", 31);
        User u4 = new User("Jack", 41);
        User u5 = new User("Tony", 31);

        set.add(u1);
        set.add(u2);
        set.add(u3);
        set.add(u4);
        set.add(u5);
        set.add(1);

        System.out.println(set);
    }

这里最后插入了一个Integer类,而不是User类,自然就没有name属性和age属性,所以会报错!!因为这样就比较不了!除非进一步更改比较器内的代码内容,让之也可以对比!

Map

Map和Collection其实是并列的,但是也有类似的地方,比如Map和Collection的子接口Set类似,不允许有重复的,而这里的重复当然指的是Map每一个单元的key不能重复。value是可以重复的。那么当我们想要操作Map中的数据的时候,会分很多种情况,比如要遍历每一个key,要遍历每一个value,或者要遍历每一个key-value。那么Map就提供了类似的方法去返回你想要的这些数据,根据其特性每个key都不能相同,所以返回的是keySet,value可以相同,所以返回的是List?,不,并不是,这里比较特殊,返回的不是List接口的实现类,而是Collection接口的对象,准确的说是Collection的抽象实现类的匿名子类的匿名对象!总而言之可以理解成就是Collection,不是List。那么既然是Collection,那么自然也是可以遍历的。而每一个key-value自然也不能相同,所以返回的是entrySet,这里的entry就可以理解成Map中的每一个对象。因此!!!我们可以看出!

  • keySet
  • valueList
  • entrySet
    返回的都是Collection!所以他们自然而然都可以调用!iterator遍历器!不要问为什么返回的刚好是这个…源码设计的就是这些方法返回collection,所以map间接也可以用iterator

HashMap

HashMap吗,跟HashSet类似,总之是根据key去判断的。当然如果key相同的话,会覆盖掉value,因此不管是修改数据,还是添加数据,都是put()方法。

TreeMap

和TreeSet也类似,也有自然排序和比较器排序,下面是有需要注意的地方的代码,同理,key的类型不同的时候会无法比较,需要修改比较器的代码,这里因为是对象中重写了compareTo方法,所以选择的是自然排序,除非去修改User类中的方法,否则会报错。或者自己写一个比较器,去重新比较。当你没有给比较器的时候,Tree就会默认调用key的自然排序方法。

@Test
    public void test2(){
        TreeMap treeMap = new TreeMap();
        set.User u1 = new set.User("Tom",11 );
        set.User u2 = new set.User("Jerry",21 );
        set.User u3 = new set.User("Rose", 31);
        set.User u4 = new set.User("Jack", 41);
        set.User u5 = new User("Tony", 31);

        treeMap.put(u1, 78);
        treeMap.put(u2, 88);
        treeMap.put(u3, 99);
        treeMap.put(u4, 76);
        treeMap.put(u5, 55);


        Set entrySet = treeMap.entrySet();
        /**
         *
         * 这里一定要注意因为Tree是根据某一值进行排序的,也就是说,如果key是个对象,而对象定义了compareTo方法,那么就会根据compareTo方法进行对比
         * 进而得出排序,和List的不一样,List是有序的插入,有序的遍历,类似于数组。而这个Tree是根据你插入的key的值进行判定的!
         * 而这里的key都是User对象,而User是是实现了Comparable接口,重写了compareTo方法,那么这里的Tree自然以commpareTo为基准进行排序
         * 而compareTo中又是以User对象的age为基准比大小的,所以当你先插入u3,再插入u5的时候,检测到这两个对象的key大小是一样的,那么就会覆盖
         * 但是!!!!只会覆盖value值,所以打印出来后,对象是u3,但是value却换成了55!!!
         */
        for (Object entry : entrySet) {
            System.out.println(entry);
        }


        /**
         * 同样,这里因为age是一样的,所以会显示true,所以懂了吗?我们在写compareTo的时候一定要注意,到底应该只针对一个属性,还是多个属性!
         */
        System.out.println(treeMap.containsKey(new map.User("Maria", 33)));
    }

Collections工具类

这个工具类是服务于Collection和Map的
这个是个工具类,Arrays也是工具类
Collections中有个sort方法,可以服务于List,sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序,这里的排序就是根据某一值取排序,所以排序完后的List,就不仅地址有序,而且值有序,不仅可以根据下标取值,而且有序的地址的值的大小是有序的!
目前就想到这么多要关注的点,其他的想到了以后再补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值