集合-Collection&List&Set

(1)集合
  • 集合体系
        |---Collection 单列集合
            |---List 有序 可重复
                |---ArrayList
                |---LinkedList
            |---Set  无序 不可重复
                |---HashSet
                |---LinkedHashSet 存取有序
                |---TreeSet 默认升序
        |---Map 双列集合
  • Collection<E> 这是单列集合的根接口
        boolean add(E e) 添加元素
        boolean remove(E e) 删除指定的元素 (如有重复删除第一个)
        boolean contains(Object obj) 判断集合中是否包含指定元素
        int size() 返回集合中元素的个数
        boolean isEmpty() 判断集合是否为空
        Object[] toArray() 将集合中元素存入一个对象数组并返回
        T[] toArray(T[]a)  将集合中元素存入一个指定类型的数组并返回(指定数组长度)
        void clear() 清空集合
       void addAll(集合) 添加另外一个集合中的元素
public class Demo2 {
    public static void main(String[] args) {
        //多态创建单列集合
        Collection<String> coll = new ArrayList();

        //boolean add(E e) 添加元素
        coll.add("关羽");
        coll.add("刘备");
        coll.add("赵云");
        System.out.println(coll);

        //boolean remove(E e) 删除指定的元素 (如有重复删除第一个)
        coll.remove("赵云");
        System.out.println(coll);

        //boolean contains(Object obj) 判断集合中是否包含指定元素
        boolean contains = coll.contains("刘备1");
        System.out.println("contains = " + contains);

        //int size() 返回集合中元素的个数
        int size = coll.size();
        System.out.println("集合长度:" + size);

        //boolean isEmpty() 判断集合是否为空
        boolean empty = coll.isEmpty();
        System.out.println("empty="+empty); //true-集合空。false-非空

        //Object[] toArray() 将集合中元素存入一个对象数组并返回
        //Object[] objects = coll.toArray();

        //T[] toArray(T[]a)  将集合中元素存入一个指定类型的数组并返回(指定数组长度)
        String[] toArray = coll.toArray(new String[coll.size()]);
        System.out.println(Arrays.toString(toArray));

        //void clear() 清空集合
        coll.clear();
        System.out.println(coll.size());
    }
}
  • 遍历1: 送代器Iterator
        单列集合专用遍历方式
  • Iterator相关方法
        Iterator<E> iterator() 获取迭代器对象,默认指向第一个元素
        boolean hasNext() 断当前位置是否有元素可以取出 (有返回true,没有返回false)
        E next() 返回当前位置的元素,并将送代器后移一位(如果没有元素可以取出了还继续取,会报NoSuchElementException)
  • 固定格式
        Iterator<String> iterator  = list.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
        }
public class Demo3 {
    public static void main(String[] args) {
        //1. 准备一个集合
        Collection<String> collection = new ArrayList<>();
        collection.add("java");
        collection.add("python");
        collection.add("c++");
        collection.add("c#");

        //2. 获取迭代器对象
        Iterator<String> iterator = collection.iterator();

        //3. 使用迭代器遍历
        //3.1 使用while,判断是否有下个元素
        while(iterator.hasNext()) {
            //3.2 获取下个元素
            String str = iterator.next();
            System.out.println(str);
        }
    }
}
  • 遍历2: 增强for循环
        数组和集合都可以使用
  • 相关格式
        for(元素数据类型 变量名 : 数组或者集合){
            操作变量
        }
  • 注意
        1. 在增强for循环中修改数据, 是不会影响数据源的(底层会创建临时变量,来记录容器中的数据)
        2. 增强for遍历集合,底层是迭代器遍历集合的逻辑
        3. 增强for遍历数组,底层是普通for循环的逻辑
public class Demo4 {
    public static void main(String[] args) {
        //1. 准备一个集合
        Collection<String> collection = new ArrayList<>();
        collection.add("java");
        collection.add("python");
        collection.add("c++");
        collection.add("c#");

        //2. 使用增强for循环遍历
        for (String s : collection) {
            System.out.println(s);
        }


        //增强for循环也可以遍历数组
        int[] arr = {1,2,3};
        for (int i : arr) {
            System.out.println(i);
        }

    }
}
  • 遍历3: Lambda表达式方式遍历集合
  • 相关格式
        collection.forEach(e -> {
            System.out.println(e);
        });
public class Demo5 {
    public static void main(String[] args) {
        //1. 准备一个集合
        Collection<String> collection = new ArrayList<>();
        collection.add("java");
        collection.add("python");
        collection.add("c++");
        collection.add("c#");

        //2. Lambda表达式方式遍历集合
        collection.forEach((e) -> {
            System.out.println(e);
        });
    }
}
/*
展示多部电影信息
    每部电影都是一个对象,多部电影要使用集合装起来。
    遍历集合中的3个电影对象,输出每部电影的详情信息。
*/
public class Demo6 {
    public static void main(String[] args) {
        //1. 创建集合
        Collection<Movie> coll = new ArrayList<Movie>();
        coll.add(new Movie("肖申克的救赎", 9.7, "弗兰克"));
        coll.add(new Movie("霸王别姬", 9.9, "陈凯歌"));
        coll.add(new Movie("阿甘正传", 9.7, "罗伯特"));
        //Movie movie1 = new Movie("热辣滚烫", 9.7, "贾玲");
        //coll.add(movie1);

        //2. 遍历输出

        //2-1 迭代器
        //获取迭代器
        Iterator<Movie> iterator = coll.iterator();
        //while循环
        while (iterator.hasNext()) {
            //获取下一个
            Movie movie = iterator.next();
            System.out.println(movie);
        }

        System.out.println("================");

        //2-2 增强for
        for (Movie movie : coll) {
            System.out.println(movie);
        }

        System.out.println("================");

        //2-3 lambda
        coll.forEach((e) -> {
            System.out.println(e);
        });
    }
}


/**
 * 影视类
 */
public class Movie {
    //名称
    private String name;
    //评分
    private double score;
    //导演
    private String director;

    public Movie(String name, double score, String director) {
        this.name = name;
        this.score = score;
        this.director = director;
    }

    public Movie() { }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }

    public String getDirector() {
        return director;
    }

    public void setDirector(String director) {
        this.director = director;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "name='" + name + '\'' +
                ", score=" + score +
                ", director='" + director + '\'' +
                '}';
    }
}

  • 问题
        使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
        由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
    
  • 解决方法
        迭代器: 用迭代器自己的删除方法删除数据即可
        增强for循环: 暂时无法解决
        普通for循环:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。
public class Demo7 {
    public static void main(String[] args) {
        //1. 准备一个集合
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("Java入门");
        arrayList.add("宁夏枸杞");
        arrayList.add("黑枸杞");
        arrayList.add("人字拖");
        arrayList.add("特级枸杞");
        arrayList.add("枸杞子");

        //使用迭代器方式:删除所有带枸杞的
        Iterator<String> iterator = arrayList.iterator();
        //循环遍历
        while (iterator.hasNext()) {
            //每个元素
            String str = iterator.next();
            if (str.contains("枸杞")) {
                //通过迭代器删除
                iterator.remove();
            }
        }

        System.out.println(arrayList);
    }
}
(2)list
  • List系列集合的特点
        有序的, 可重复
  • List集合支持索引,所以提供了很多通过索引操作元素的方法
        void add(int index,E e) 在此集合中的指定位置插入指定的元素
        E remove(int index) 删除指定索引处的元素,返回被删除的元素 (一般不接收)
        E set(int index,E e) 修改指定索引处的元素,返回被修改的元素(一般不接收)
        E get(int index) 返回指定索引处的元素
public class Demo1 {
    public static void main(String[] args) {
        //多态方式创建list集合
        List<String> list = new ArrayList<String>();
        list.add("关羽"); //0
        list.add("张飞"); //1
        list.add("赵云"); //3
        list.add("刘备"); //4
        //void add(int index,E e) 在此集合中的指定位置插入指定的元素
        list.add(2,"曹操"); //2
        System.out.println(list);
        //E remove(int index) 删除指定索引处的元素,返回被删除的元素 (一般不接收)
        list.remove(4);
        System.out.println(list);
        //E set(int index,E e) 修改指定索引处的元素,返回被修改的元素(一般不接收)
        list.set(1,"张飞飞");
        System.out.println(list);
        //E get(int index) 返回指定索引处的元素

        System.out.println(list.get(1));
        //System.out.println(list);
    }
}
  • List支持的遍历方式
        1. 迭代器
        2. 增强for循环
        3. Lambda表达式
        4. for循环(因为List集合有索引)
    
public class Demo2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("张三");
        list.add("李四");
        list.add("王五");
        list.add("王五");


        //1. 迭代器
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String s = iterator.next();
            System.out.println(s);
        }
        System.out.println("---------------");

        //2. 增强for循环
        for (String s : list) {
            System.out.println(s);
        }
        System.out.println("---------------");

        //3. Lambda表达式
        list.forEach(e ->{
            System.out.println(e);
        });

        System.out.println("---------------");

        //4. for循环(因为List集合有索引)
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}
  • ArrayList
        底层数据结构:
            基于数组实现
        特点:
            1. 查询速度快(通过索引直接定位)
            2. 增删效率低(增删的时候,需要移动增删元素后面的元素, 有时还需要进行扩容)
        适用场景:
            1. ArrayList适合于根据索引查询数据, 或者数据量不大的场景
            2. ArrayList不适合于数据量大, 同时又要频繁进行增删操作的场景
        底层原理:
           1. 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
                ArrayList<String> list = new ArrayList();
           2. 添加第一个元素时,底层会创建一个新的长度为10的数组
                list.add("a");
           3. 存满时,会扩容1.5倍
                比如存入第11个元素的时候, 长度会扩容到15
           4. 如果一次添加多个元素, 1.5倍还放不下, 则新创建数组的长度以实际为准
                比如原来是10个元素,现在又要存入10个, 则长度会扩容到20
    
  • LinkedList
        底层数据结构:
            基于双向链表实现(内存地址不连续,每个元素记录自己的前后元素)
        特点:
            1. 查询速度慢
            2. 增删效率高
            3. 对于首尾元素进行增删改查的速度都是极快的
        应用场景:
            1. 用来设计队列(两端开口,类似于一个管道,先进先出)
                只操作首尾元素, 尾部添加, 首部删除
            2. 用来设计栈(一段开口,类似于弹夹,先进后出)
public class Demo4 {

    public static void main(String[] args) {
        //makeQueue();
        makeStack();
    }

    /*
        队列: 两端开口,特点是先进先出(排队)
        从队列后端入队列:  addLast 方法
        从队列前端出队列:  removeFirst方法
    */
    public static void makeQueue() {

        LinkedList<String> queue = new LinkedList<>();

        //排队买票 (入队)
        queue.addLast("1号顾客"); //1号顾客
        queue.addLast("2号顾客"); //1号顾客,2号顾客
        queue.addLast("3号顾客"); //1号顾客,2号顾客,3号顾客
        queue.addLast("4号顾客"); //1号顾客,2号顾客,3号顾客,4号顾客
        queue.addLast("5号顾客"); //1号顾客,2号顾客,3号顾客,4号顾客,5号顾客

        System.out.println(queue);

        System.out.println("--------------");

        //放票-出对
        String str1 = queue.removeFirst();
        System.out.println(str1);
        String str2 = queue.removeFirst();
        System.out.println(str2);
        String str3 = queue.removeFirst();
        System.out.println(str3);

        System.out.println(queue);
    }

    /*
        栈: 顶端开口的结构,特点是先进后出
        进栈/压栈: push方法(底层封装了addFirst 方法)
        出栈/弹栈: pop方法底 (底层封装了removeFirst方法)
    */
    public static void makeStack() {
        //创建LinkedList
        LinkedList<String> stack = new LinkedList<>();
        //压栈(push):将数据存入栈中
        stack.push("关羽");
        stack.push("张飞");
        stack.push("赵云");
        System.out.println(stack);
        System.out.println("-------------------");
        //弹栈(pop):将栈顶数据移除
        String str1 = stack.pop();
        System.out.println(str1); //赵云
        System.out.println(stack);
    }
}
(3)set
  • List系列集合的特点
        无序, 不可重复
    
  • 子类
        HashSet: 完美继承
        LinkedHashSet: 存取有序
        TreeSet: 可以排序
public class Demo1 {
    public static void main(String[] args) {
        //testHashSet();
        //testLinkedHashSet();
        testTreeSet();
    }

    //HashSet: 无序、没有索引、不可重复
    private static void testHashSet() {
        HashSet<Integer> set =  new HashSet();
        set.add(44);
        set.add(25);
        set.add(6);
        set.add(29);
        set.add(44);
        System.out.println(set);
    }

    //LinkedHashSet: 存取有序、没有索引、不可重复
    private static void testLinkedHashSet() {
        LinkedHashSet<Integer> linkedHashSet = new LinkedHashSet<>();
        linkedHashSet.add(44);
        linkedHashSet.add(33);
        linkedHashSet.add(11);
        linkedHashSet.add(22);
        linkedHashSet.add(22);
        System.out.println(linkedHashSet);
    }

    //TressSet: 排序、没有索引、不可重复
    private static void testTreeSet() {
        TreeSet<String> treeSet = new TreeSet<>();

        treeSet.add("book");
        treeSet.add("dog");
        treeSet.add("cat");
        treeSet.add("apple");


//        treeSet.add(44);
//        treeSet.add(33);
//        treeSet.add(11);
//        treeSet.add(22);
//        treeSet.add(22);
        System.out.println(treeSet);
    }
}
  • 哈希值
        就是一int值,Java每个对象都可以通过hashCode方法,获取自己的哈希值
    
  • 哈希值特点
        同一个对象多次调用hashCode方法,返回的哈希值是相同的;
        不同的对象,他们哈希值大几率不相同,但是也有可能会相同(哈希碰撞)
        Object的hashCode方法根据"对象地址值"计算哈希值,子类重写后的hashCode方法可以根据"对象属性值"计算哈希值
使用场景
    HashSet集合判定两个对象的标准就是两个对象的hash值是否一致, 因此我们经常重写hashcode实现集合中对象去重

public class Demo2 {
    public static void main(String[] args) {
//        //1、创建Student对象,获取对象的hash值
//        Student student = new Student("张三", 18);
//        int code1 = student.hashCode();
//        int code2 = student.hashCode();
//        //同一个对象多次调用hashCode方法,返回的哈希值是相同的;
//        System.out.println(code1);
//        System.out.println(code2);
//        //2、不同的对象,他们哈希值大几率不相同,但是也有可能会相同(哈希碰撞)
//        Student student2 = new Student("张三", 18);
//        int code3 = student2.hashCode();
//        System.out.println(code3);

        //通过HashSet集合,存储学生数据(去重)
        HashSet<Student> set = new HashSet();
        //构造几个学生对象,存入到set
        Student student1 = new Student("张三", 18);
        Student student2 = new Student("李四", 28);
        Student student3 = new Student("张三", 18);

        System.out.println(student1.hashCode());
        System.out.println(student3.hashCode());

        System.out.println(student1.toString());

        System.out.println(student3.toString());

        set.add(student1);
        set.add(student2);
        set.add(student3);
        //打印set中的数据
        System.out.println(set);
    }
}

class Student {

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age && Objects.equals(name, student.name);
    }

}
  • HashSet集合的底层原理
        JDK8之前: 数组+链表
        JDK8开始: 数组+链表+红黑树
    
  • 树结构
        二叉树: 基本模型
            每一个根节点最多只能有两个子节点,子节点数量称为度,树的总层数为树高
            左子节点
            右子节点
            左子树
            右子树
  • 二叉查找/二又搜索树: BST (Binary Search  Tree)
        小的存左边,大的存右边,相同则不存
        如果数据已经排好序,那么存入二又树查找树,会形成类似链表结构,查询效率还是低
        如果左、右子树的树高差比较大,那么导致左右子树不平衡,影响查询效率
  • 平衡二叉树: AVL (Balanced  Binary Tree)
        有规律且相对平衡的二叉树
        当插入一个元素,导致左右子树的树高差大于1。那么就会触发旋转
        旋转分为左旋和右旋,用来保证左右子树的相对平衡
    
  • 红黑: RBT (Red-Black  Tree)
        特殊的/自平衡的二又查找树,是计算机科学中用到的一种数据结构
        1972年出现时被称为平衡二叉树,后来1978年被修改为如今的红黑树
        红黑树不是高度平衡的,有自己保证平衡的规则(红黑规则),性能较好,红黑规则如下:
        每一个节点都是红色或者黑色的
        根节点必须是黑色的
        两个红色节点不能相连
        如果一个节点没有子节点,则该节点相应的指针属性为Nil (称为叶子结点),叶子结点是黑色的
        对于每一个节点,到其所有后代叶节点的简单路径上,包含的黑色 节点数量相同
  • TreeSet 可排序、不重复、无索引
        底层基于红黑树实现排序,排序规则认为属性是相同的对象则不存
  • TreeSet的排序
        对于数值型Integer、Double,默认按照数值升序排列;
        对于String类型数据,默认按照字典排序
        对于自定义类,默认是无法排序的,需要我们指定排序规则
            自然排序:自定义类实现Comparable接口,重写compareTo方法,指定排序规则
            比较器排序:写在TreeSet构造参数中传递Comparator比较器对象,重写compare方法,指定排序规则
  • 需求
        使用TreeSet存储教师对象,重复对象不存,并且用两种方式按照年龄升序排列
public class Demo4 {

    public static void main(String[] args) {
        //创建TreeSet
        Set<Teacher> treeSet = new TreeSet<>(new Comparator<Teacher>() {
            @Override
            public int compare(Teacher o1, Teacher o2) {
                return o1.getAge() - o2.getAge();
            }
        });

        //添加学生
        treeSet.add(new Teacher("张三", 19));
        treeSet.add(new Teacher("李四", 18));
        treeSet.add(new Teacher("王五", 20));
        treeSet.add(new Teacher("赵六", 17));
        treeSet.add(new Teacher("赵六", 17));

        //打印
        for (Teacher teacher : treeSet) {
            System.out.println(teacher);
        }
    }
}

class Teacher  {


    private String name;
    private int age;

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Teacher teacher = (Teacher) o;
        return age == teacher.age && Objects.equals(name, teacher.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值