美文网首页
3/11day08_List接口_Set接口_Collectio

3/11day08_List接口_Set接口_Collectio

作者: 蹦蹦跶跶的起床啊 | 来源:发表于2020-03-11 16:13 被阅读0次

    复习

    1.Collection根接口
        增: boolean add(E e)
        删: boolean remove(Object obj)
        改: 无
        查: 无
        其他: clear isEmpty size contains toArray
    2.迭代器: 遍历集合(与索引无关)
        Iterator<集合的泛型> it = 集合对象.iterator();
        while(it.hasNext()){
            集合的泛型 变量名 = it.next();   
        }
        增强for循环,就是迭代器的语法糖(简便格式)
        for(数据类型 变量名 : 集合/数组){
            System.out.println(变量名);
        }
        =========================
        注意:使用迭代器的过程中,你不能直接通过代码对集合的元素进行增删操作.
        如果你想增删元素,a.使用普通的fori循环 b.使用迭代器提供remove方法    
        
    3.泛型(理解)
         给一个泛型类,比如ArrayList<E>,怎么使用??
            ArrayList<String> arr = new ArrayList<String>();
            ArrayList<Integer> arr = new ArrayList<Integer>();
    4.4+1种数据结构
        栈结构: 先进后出
        队列结构: 先进先出
        数组结构: 查询快,增删慢
        链表结构: 查询慢,增删快
        红黑树结构: 查询速度非常恐怖    
    

    今日内容

    • List接口
      List接口的实现类(ArrayList,LinkedList,Vector)
    • Set接口
      Set接口的实现类(HashSet,LinkedHashSet,TreeSet)
    • Collections 专门操作集合的工具类
      打乱顺序,排序(默认),自定义排序

    List接口[重点]

    List接口特点

    • 继承了Collection接口
    • List接口的特点:
      1.有序的(Java中的有序的是指存取顺序保持一致,如存入2,1,3 取出也是2,1,3)
      2.有索引的
      3.元素可重复的

    List接口中常用方法

    • 继承Collection接口, 所以有Collection中的8个(7个常见,1个获取迭代器)
    • List接口自己的特有方法(跟索引相关)
      增: add(int index, E e)
      删:remove(int index)
      改:set(int index, E 新元素);
      查:get(int index)
    • List接口的实现类
      1.ArrayList集合 [重点]
      2.LinkedList集合[重点]
      3.Vector[了解]

    ArrayList的数据结构以及使用

    特点: 查询快,增删慢!!

    • ArrayList集合的方法(7个Collection中的+1个迭代器+4个List中的)
    • 数据结构:ArrayList集合底层采用的是数组结构.因此查询快,增删慢

    LinkedList集合的数据结构以及使用

    特点:查询慢,增删快

    • LinkedList的方法(7个Collection中的+1个迭代器+4个List接口中的) 特有方法有8个:
    public void addFirst(E e) :将指定元素插入此列表的开头。
    public void addLast(E e) :将指定元素添加到此列表的结尾。
    public E getFirst() :返回此列表的第一个元素。
    public E getLast() :返回此列表的最后一个元素。
    public E removeFirst() :移除并返回此列表的第一个元素。
    public E removeLast() :移除并返回此列表的最后一个元素。
    public E pop() :将指定元素添加到此列表的结尾。从此列表所表示的堆栈处弹出一个元素。底层为removeFirst()和此方法一样
    public void push(E e) :将指定元素插入此列表的开头。将元素推入此列表所表示的堆栈。底层为addFirst() 和此方法一样
    public boolean isEmpty() :如果列表不包含元素,则返回true。
    
    • LinkedList集合数据存储的结构是链表结构方便元素添加、删除的集合。

    • LinkedList的使用

    LinkedList的源码分析(了解)

    • LinkedList的底层采用链表结构(双向链表)
    • LinkedList这个类有两个成员变量
      Node<E> first; 记录了开始节点
      Node<E> last; 记录了结束节点
    • 节点类Node,是这样的
      它是LinkedList的内部类
      private static class Node<E>{
      E item; 该节点的数据域
      Node<E> prev; 指针域,指向上一个节点
      Node<E> next; 指针域,指向下一个节点
      }
    • LinkedList的add方法
      a.将新增的节点,添加到last节点之后
      b.并且将size++,总的元素个数增加1
    • LinkedList的get方法
      a.先查找指定索引的那个节点(从前往后找,从后往前找)
      b.找到节点之后,获取节点的数据域,然后返回

    Collections类(重点)

    Collections的介绍

    是一个集合的工具类,该类中的很多静态方法用来操作集合

    Collections的常用功能

    - public static void shuffle(List<?> list) :打乱集合顺序。
    - public static <T> void sort(List<T> list,Comparator<T> com); 带有比较器的排序方法
    如果集合泛型是数值类型, 按照数值大小升序(比较器详细讲解在下边)
    如果集合泛型是Chararcter类型,那按照字符的码值升序
    如果集合泛型是String类型,那么按照首字母升序,如果首字母一样,按照次字母排序,以此类推
    - public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照指定规则排序。
    

    注意:
    sort、max、min三个方法都需要指定排序的规则。即:如果集合中存的是自定义对象,那么该对象必须要实现Comparable接口或者给这三个方法传递一个Comparator的实现类作为比较器.

    使用Collections类的sort方法

    使用带有比较器的sort方法:

    //使用比较器降序排序
    public class TestCollections02 {
        public static void main(String[] args) {
            //1.创建一个集合
            ArrayList<Integer> arr = new ArrayList<Integer>();
            //2.添加点元素
            arr.add(2);
            arr.add(8);
            arr.add(3);
            arr.add(9);
            arr.add(4);
            arr.add(1);
            arr.add(6);
            arr.add(7);
            arr.add(5);
            System.out.println(arr);
            //3.使用带有比较器的sort方法
            Collections.sort(arr, new Comparator<Integer>() {
                /**
                 * 该方法就是比较方法
                 * @param o1 第一元素
                 * @param o2 第二元素
                 * @return 返回值代表谁大谁小,如果是正数代表o1大,如果适合负数代表o2大,如果是0代表一样大
                 *
                 * 口诀:
                 *  升序 前-后
                 */
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2-o1;
                }
            });
            System.out.println(arr);
        }
    }
        
    //使用比较器排序自定义类型
    public class TestCollections03 {
        public static void main(String[] args) {
            //1.创建一个集合
            ArrayList<Dog> arr = new ArrayList<Dog>();
            //2.加狗
            arr.add(new Dog(1,"jack",4));
            arr.add(new Dog(4,"lilei",3));
            arr.add(new Dog(2,"ady",2));
            arr.add(new Dog(3,"hanmmeimei",5));
            //3.对狗集合继续排序
            Collections.sort(arr, new Comparator<Dog>() {
                @Override
                public int compare(Dog o1, Dog o2) {
                    //升序 前-后
                    //按照狗的年龄降序
    //                return o2.age-o1.age;
                    //按照狗的名字的长度升序
    //                return o1.name.length()-o2.name.length();
                    //按照狗的年龄和腿数的总和,升序
                    return (o1.age+o1.legs)-(o2.age+o2.legs);
    
                }
            });
            //4.打印
            for (Dog dog : arr) {
                System.out.println(dog);
            }
        }
    }
    

    排序

    自然排序和比较器排序

    • 整数类型(int,short,byte,long) 可以用减法,
    • 引用类型 使用 对象名.compareTo(对象名)方法

    自然排序(只能实现一种排序规则)

    根据数字的大小

    Comparable接口

    在定义类中实现Comparable接口,并且重写该接口内的比较规则的方法 然后在测试类中可以配合 Conllections.sort().使用,打印出来的集合就是按照了自定义的比较规则而打印
    也可以配合TreeSet集合使用

    自然排序缺点:

    只能实现一种比较规则. 所以比较规则多的话使用比较器排序

    图解代码

    比较器排序

    比较规则写在类的内部,每个比较器都可以实现一个自定义比较规则,想要用多种规则,就用多个比较器实现,一般使用匿名内部类填写规则.

    Comparator接口(比较器)

    Collections中有两个sort重载方法, 一个根据从小到大排序, 一个根据规则排序
    public static <T> void sort(List<T> list,Comparator<? super T> ) :将集合中元素按照指定规则排序。参数列表中, 一个为集合, 一个为接口实现类对象.接口参数需要传入接口的实现类对象或者使用匿名内部类实现.
    此匿名内部类中的返回值口诀为: 升序前-后

    public static void main(String[] args) {
            // 创建四个学生对象 存储到集合中
            ArrayList<Student> list = new ArrayList<Student>();
     
            list.add(new Student("rose",18));
            list.add(new Student("jack",16));
            list.add(new Student("abc",20));
            Collections.sort(list, new Comparator<Student>() {
              @Override
                public int compare(Student o1, Student o2) {
                这里的返回值,升序 前-后
                return o1.getAge()-o2.getAge();//以学生的年龄升序
          或者使用 return o1.name.compareTo(o2.name) 字符串已经实现好了 compareTo方法
             }
            });
     
     
            for (Student student : list) {
                System.out.println(student);
            }
        }
    

    可变参数

    • 使用
      在JDK1.5之后,如果我们定义一个方法需要接受多个参数,并且多个参数类型一致,我们可以对其简化.
      格式:

    修饰符 返回值类型 方法名(参数类型... 形参名){ }

    注意:
    1.一个方法只能有一个可变参数
    2.如果方法中有多个参数,可变参数要放到最后。

    • 应用场景
      Collections
      在Collections中也提供了添加一些元素方法:
      public static <T> boolean addAll(Collection<T> c, T... elements) :往集合中添加一些元素。
    public class CollectionsDemo {
        public static void main(String[] args) {
          ArrayList<Integer> list = new ArrayList<Integer>();
          //原来写法
          //list.add(12);
          //list.add(14);
          //list.add(15);
          //list.add(1000);
          //采用工具类 完成 往集合中添加元素  
          Collections.addAll(list, 5, 222, 1,2);
          System.out.println(list);
    }
    

    Set接口[重点]

    Set接口的特点

    • 继承Collection接口
    • 特点:
      1.无序.(实现类LinkedHashSet除外)(Java中无序指,存入2,1,3. 打印出来可能是1,2,3等)
      2.无索引
      3.元素不可重复
      注意: 除了Set接口的实现类LinkedHashSet是有序的

    Set接口的常用方法以及常用子类

    • Set接口中的方法(7个Collection继承的常见方法,1个迭代器)
    • Set接口没有特有方法
    • Set接口的实现类
      1.HashSet
      2.LinkedHashSet
      3.TreeSet

    HashSet的数据结构以及使用

    • HashSet没有特有方法
    • HashSet底层采用的是哈希表结构(数组结构+链表结构+红黑树结构)无序,无索引,元素唯一
    • HashSet的使用
    public class TestHashSet {
        public static void main(String[] args) {
            //1.创建一个HashSet对象
            HashSet<String> set = new HashSet<String>();
            //2.添加,没有带索引的方法,证明无索引
            set.add("php");
            set.add("java");
            set.add("python");
            set.add("c++");
            set.add("c#");
            //3.直接的打印,证明无序
            System.out.println(set);//[c#, python, c++, java, php]
            //4.再加一个一样元素,证明唯一
            set.add("php");
            set.add("php");
            set.add("php");
            set.add("php");
            set.add("php");
            set.add("php");
            System.out.println(set);
        }
    }    
    

    哈希表结构的介绍

    • Java中的每个对象的哈希值(对象的"数字指纹")
      1.对象的哈希值,相当于对象的"指纹"(哈希值是十进制, 对象的地址值是十六进制 , 换算之后值一样)
      2.获取对象的哈希值
      调用对象.hashCode方法就可以获取 返回值为int
    public class TestHashDemo01 {
        public static void main(String[] args) {
            //1.获取一个对象的哈希码值
            Student s1 = new Student(10,"前妻");
            int hashCode = s1.hashCode();
            System.out.println(hashCode);//1163157884
        }
    }  
       
    

    3.Java中的地址值真实面目是哈希值的十六进制表示
    4.Java中的地址值存在,但是看不见
    Dog d = new Dog(); 变量d中存储的是Dog()对象的真正地址值. 打印d 是Object类中的toString方法此方法是打印出哈希值. 因此隐藏了对象的地址值.
    5.不同的两个对象,可能会有哈希值相同的情况, 因为获取的哈希值方法返回值为int类型, 对象可以有无数个, 哈希值只有int范围个.(特别是String类会有可能出现地址值相同如 "abc"和"acD")

    • 哈希表结构特点(无序,无索引,无重复)
      1.哈希表结构会先比较两个对象的哈希值,再调用equals比较两个对象.只有哈希值相同,并且equals返回值为true.才判定这两个元素为重复的,后进来的元素不再进行添加操作(具体底层操作如下图)
      2.哈希表结构 = 数组结构 + 链表结构 +红黑树结构


    • 哈希值(上图解释)
      因为哈希表结构底层使用了数组结构, 所以哈希值决定了元素放入数组的哪个位置
      HashSet中使用了HashMap

    如 键:a,hash:97 计算方法为: hash%数组长度=需要放置的数组下标 97%16 = 6 所以将元素a放在数组下标为6的位置. 如果哈希值一样, 计算出来的下标一样的话, 就用链表形式,当多于8个链表的时候,采用了红黑树结构

    哈希表结构保存自定义类型[重要]

    因为哈希表判断两个元素重复与否, 先判断的是哈希值,再使用equals方法判断.
    所以为了保证元素的唯一性, 如果元素是自定义类型, 必须重写hashCode和equals方法 (alt+insert)快捷键

    /**
     * 使用哈希表保存自定义类型的练习
     */
    public class TestHashSetDemo {
        public static void main(String[] args) {
            //1.创建一个哈希表结构的集合
            HashSet<Dog> dogHashSet = new HashSet<Dog>();
            //2.保存对象
            dogHashSet.add(new Dog(10,"旺小财",3));
            dogHashSet.add(new Dog(20,"旺中财",4));
            dogHashSet.add(new Dog(30,"旺大财",5));
            dogHashSet.add(new Dog(40,"旺老财",6));
    
            //再添加一只狗
            dogHashSet.add(new Dog(30,"旺大财",5));
            //哈希表判断两个元素重复or不重复的依据是什么??
            //  哈希表和equals
            //为了保证元素的唯一性,我们要重写hashCode和equals,根据内容来计算哈希值,equals也改成比较内容
            //3.打印
            for (Dog dog : dogHashSet) {
                System.out.println(dog);
            }
        }
    }
    
    public class Dog {
        int age;
        String name;
        int legs;
        public Dog() {
        }
        public Dog(int age, String name, int legs) {
            this.age = age;
            this.name = name;
            this.legs = legs;
        }
        @Override
        public String toString() {
            return "Dog{" +
                    "age=" + age +
                    ", name='" + name + '\'' +
                    ", legs=" + legs +
                    '}';
        }
        //为了保证哈希表中元素的唯一性,我们需要重写hashCode和equals方法
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Dog dog = (Dog) o;
            return age == dog.age &&
                    legs == dog.legs &&
                    Objects.equals(name, dog.name);
        }
        @Override
        public int hashCode() {
            return Objects.hash(age, name, legs);
        }
    }
    

    HashSet源码分析

    • 构造方法
    HashSet<String> set = new HashSet<String>();
        public HashSet() {
            map = new HashMap<>(); 
        }
    

    HashSet底层实际是依赖一个HashMap

    • HashSet的add方法
    public boolean add(E e) { 
            return map.put(e, PRESENT)==null;
        }     
    

    实际上是调用HashMap的put方法

    • HashMap的put方法
      HashMap保存键时,是根据键的哈希值确定保存位置.

    LinkedHashSet的数据结构以及使用

    • LinkedHashSet特点:有序,无索引,元素不重复
    • LinkedHashSet无特有方法
    • LinkedHashSet底层采用链表+哈希表结构
    • LinkedHashSet的使用)

    TreeSet的数据结构以及使用

    • TreeSet的特点
      1.无序的但是有自然顺序(存取顺序不一样, 但是无序中一种特殊存在,有自然顺序, 打印出的值是按自然数值从小到大打印出来, 如 输入 321, 打印出来为123)
      2.无索引的
      3.元素唯一
      按照元素大小进行排序,要求元素实现Comparable接口(自然排序)或者加比较器
    • TreeSet特有方法: 没有
    • TreeSet底层采用红黑树结构
    • TreeSet的使用
    public static void main(String[] args) {
        //无参构造,默认使用元素的自然顺序进行排序
        TreeSet<Integer> set = new TreeSet<Integer>();
        set.add(20);
        set.add(18);
        set.add(23);
        set.add(22);
        set.add(17);
        set.add(24);
        set.add(19);
        System.out.println(set);
    }
     
    控制台的输出结果为:
    [17, 18, 19, 20, 22, 23, 24]
    
    • TreeSet也可以使用比较器自定义排序规则
      因为TreeSet的构造方法有两个
    public TreeSet():                               根据其元素的自然排序进行排序
    public TreeSet(Comparator<E> comparator):    根据指定的比较器进行排序
    

    相关文章

      网友评论

          本文标题:3/11day08_List接口_Set接口_Collectio

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