集合(主要涉及Collection)

106 阅读4分钟

foreach循环:

image.png

        //方式1:普通for循环
//        for (int i = 0;i < arr.length;i++){
//            arr[i]="GG";
//        }
//        for (int i = 0; i < arr.length; i++) {
//            System.out.println(arr[i]);输出:GG GG GG

       // }
        //方式2:增强for循环
        for(String s : arr){
            s = "GG";
        }
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }//输出:MM MM MM
    }
}

image.png

List接口

//        ----List接口:存储有序,可重复的数据。“动态数组”
//        ----ArrayList:作为List接口的主要实现类,线程不安全,效率高,底层使用Object[] elementData存储
//        ----LinkedList:对于频繁的插入,删除操作,但是遍历,查找效率不高。使用此类效率比ArrayList高。底层使用双向链表存储
//        ----Vector:作为List接口的古老实现类,线程安全,效率低,底层使用Object[] elementData存储

//   面试题:三者的异同
//    同:都实现了List接口,存储数据特点相同
//    不同:在上面有所体现

image.png

ArrayList的源码分析

        jdk7.0情况下
        ArrayList list = new ArrayList();//底层创建了长度是10的Object数组elementData
        List.add(123);//elementData[0]=new Integer(123)
        ...
        list.add(11)//如果此次添加导致底层elementData数组容量不够,则进行扩容
        默认情况下,扩容为原来的1.5倍,同时需要将原有的数据复制到新的数组中

        结论:建议开发中使用带参的构造器:Arrayslist list = new ArrayList(int capacity)

        jdk8.0情况下的变化:
        ArrayList list = new ArrayList();//此时底层Object[]elementData初始化为{},并没有创建长度为10的数组
        list.add(123)//此时才创建了底层长度为10的数组,并将数据123添加到elementData[0]
        ...
        后续的添加与扩容与jdk7.0相同

        小结:jdk7中ArraysList的对象的创建类似于单例模式中的饿汉式。
        jdk8中ArraysList的对象的创建相当于懒汉式,延迟了数组的创建,节省了内存

 */

LinkedList源码分析

         LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
         list.add(123);//将123封装到Node中,创建了Node对象

         其中Node定义为:双向链表
         private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }

image.png

public void test(){
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add("AA");
        list.add(new Person("Tom",12));
        list.add(456);
        System.out.println(list);

        //1.void add(int index,Object ele):在index位置插入ele元素
        list.add(1,"BB");

        //2.boolean addAll(int index,Collection eles):从index位置开始将eles中所有元素添加到
        List list1 = Arrays.asList(1, 2, 3);
        list.addAll(list1);
        System.out.println(list.size());//9

        //3.Object get(int index):获取指定index位置的元素
        System.out.println(list.get(0));//123
    }
}
    ArrayList list = new ArrayList();
    list.add(123);
    list.add(456);
    list.add("AA");
    list.add(new Person("Tom", 12));
    list.add(456);

    //4.int indexOf(Object obj):返回obj在集合中首次出现的位置,如果不存在,返回-1
    int index = list.indexOf(456);
    System.out.println(index);//1

    //5.int lastIndexOf(Object obj):返回obj在集合中末次出现的位置,如果不存在,返回-1
    int index1 = list.lastIndexOf(456);
    System.out.println(index1);//4

    //6.Object remove(int index):移除指定位置的元素,并返回该元素
    Object remove = list.remove(0);
    System.out.println(remove);//123
    System.out.println(list);//[456, AA, Person{name='Tom', age=12}, 456]

    //7.Object set(int index,Object ele):设置指定index位置的元素为ele
    list.set(1,"CC");
    System.out.println(list);//[456, CC, Person{name='Tom', age=12}, 456]

    //8.subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合
    List list1 = list.subList(2, 4);
    System.out.println(list1);//[Person{name='Tom', age=12}, 456]
}

List类的常用方法

增:add(Object obj)

删:remove(int index)/remove(Object obj)

改:set(int index,Object ele)

查:get(int index)

插:add(int index,Object ele)

长度:size()

遍历:①Iterator迭代器方式

②增强for循环

③普通循环

Set接口

image.png

image.png

image.png

image.png

七上八下:jdk7时新在上,jdk8时新在下

1.Set中没有定义新的方法,使用的都是Collection中声明过的方法
2.要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals()
要求:重写的hashCode()和equals()尽可能保持一致性

一丶Set:存储无序的,不可重复的数据
以HashSet为例说明:
1.无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的


2.不可重复性:保证添加的元素按照equals()判断时,不能返回true 即:相同的元素只能添加一个
而自定义类需要重写equals()方法

二丶添加元素的过程:以HashSet为例说明
    我们向HashSet中添加元素a,首先调用元素a所在类的hashCode方法,计算a的哈希值
    此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置)
    判断数组此位置上是否已经有元素,
        如果此位置上没有其他元素,则a添加成功--->添加成功的情况1
        如果此位置上有其他元素b(或者以链表形式存在的多个元素),则比较a与b的哈希值:
            如果哈希值不相同,则a添加成功。--->添加成功的情况2
            如果哈希值相同,进而调用a所在类的equals方法
                equals返回true,表明a添加失败
                equals返回false,则a添加成功--->添加成功的情况3

      对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表方式存储
      jdk7:a放到数组中,指向原来的元素
      jdk8:原来的元素在数组中,指向a
      总结:七上八下
      
      HashSet底层:数组+链表
 */

image.png

image.png

LinkedList

    //LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据后一个数据
    //对于频繁的遍历操作,LinkedHashSet效率高于HashSet
    public void test1(){
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(new User("Tom",12));
        set.add(new User("Tom",12));
        set.add(129);

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

image.png

        TreeSet set = new TreeSet();
            //失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new Person("TOM",12));

        //举例1:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);

        //举例2:
        set.add(new User("TOM",12));
        set.add(new User("JERRY",32));
        set.add(new User("MIKE",2));
        set.add(new User("JIM",65));
        set.add(new User("JACK",33));
        set.add(new User("JACK",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    Class User{
     //按照姓名从大到小排列,年龄从小到大排序
    public int compareTo(Object o) {
        if(o instanceof User){
            User user = (User) o;
            int compare =  -this.name.compareTo(user.name);
        if(compare!=0){
            return compare;
        }else{
            return Integer.compare(this.age,user.age);
        }
        }else {
            throw new RuntimeException("输入的类型不匹配");
        }
    }
    }
    Comparator com = 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;
                return Integer.compare(u1.getAge(),u2.getAge());
            }else{
                throw new RuntimeException("输入的类型不匹配");
            }
        }
    };
    TreeSet set = new TreeSet(com);
    set.add(new User("TOM",12));
    set.add(new User("JERRY",32));
    set.add(new User("MIKE",2));
    set.add(new User("JIM",65));
    set.add(new User("JACK",33));
    set.add(new User("JACK",56));


    Iterator iterator = set.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }

}
}

4个注意点

1.向TreeSet中添加的数据,要求是相同类的对象

2.两种排序方式:自然排序(实现Comparable接口)和定制排序

3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals方法

4.定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals方法