day06-集合-Collection&List&Set

一、集合

Collection集合特点:

  • list系列集合:有序、可重复

    • ArrayList、LinekdList
  • Set系列集合:无序、不重复

    • HashSet

    • LinkedHashSet:存取有序

    • TreeSet:可排序

1.1 Collection的常见方法

复制代码
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(集合) 添加另外一个集合中的元素
java 复制代码
public class Demo2 {
    public static void main(String[] args) {
        //多态创建单列集合
        Collection<String> coll = new ArrayList<>();
        //boolean add(E e) 添加元素
        coll.add("下雨");
        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("下雪");
        System.out.println("contains=" + contains);
​
        //int size() 返回集合中元素的个数
        int size = coll.size();
        System.out.println("size=" + size);
​
        //boolean isEmpty() 判断集合是否为空
        boolean empty = coll.isEmpty();
        System.out.println("empty=" + empty);//空-->true;否则-->false
​
        //Object[] toArray() 将集合中元素存入一个对象数组并返回
        Object[] objects = coll.toArray();
        System.out.println(Arrays.toString(objects));
​
        //T[] toArray(T[]a)  将集合中元素存入一个指定类型的数组并返回(指定数组长度)
        String[] strings = coll.toArray(new String[coll.size()]);
        System.out.println(Arrays.toString(strings));
​
        //void clear() 清空集合
        coll.clear();
        System.out.println(coll);
​
    }
}
运行结果:
[下雨, 下雪, 刮风, 刮风]
[下雨, 下雪, 刮风]
contains=true
size=3
empty=false
[下雨, 下雪, 刮风]
[下雨, 下雪, 刮风]
[]

1.2 Collection的遍历方式

1.2.1 迭代器Iterator

复制代码
遍历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();
 }
java 复制代码
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. 使用迭代器遍历
        while(iterator.hasNext()){
            String s = iterator.next();
            System.out.println(s);
        }
    }
}

1.2.2 增强for

复制代码
遍历2: 增强for循环
 数组和集合都可以使用
​
相关格式
 for(元素数据类型 变量名 : 数组或者集合){
     操作变量
 }
​
注意
    1. 在增强for循环中修改数据, 是不会影响数据源的(底层会创建临时变量,来记录容器中的数据)
    2. 增强for遍历集合,底层是迭代器遍历集合的逻辑
    3. 增强for遍历数组,底层是普通for循环的逻辑
java 复制代码
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);
        }
    }
}

1.2.3 Lambda表达式方式

复制代码
遍历3: Lambda表达式方式遍历集合
​
相关格式
 collection.forEach(e -> {
     System.out.println(e);
 });
java 复制代码
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)->{
            //遍历集合中的元素e
            System.out.println(e);
        });
​
    }
}

1.3 删除集合元素

复制代码
问题
 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
 
 由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
​
解决方法
 迭代器: 用迭代器自己的删除方法删除数据即可(iterator.remove();)
 增强for循环: 暂时无法解决
 普通for循环(需要有索引):可以倒着遍历并删除;或者从前往后遍历,但删除元素后做(i--)操作。
java 复制代码
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);//[Java入门, 人字拖]
    }
}

1.4 集合存储对象的原理

二、List集合

2.1 List集合常用方法

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

2.2 list集合遍历

复制代码
List支持的遍历方式
    1. 迭代器
    2. 增强for循环
    3. Lambda表达式
    4. for循环(因为List集合有索引)
java 复制代码
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++) {
            String s = list.get(i);
            System.out.println(s);
        }
​
​
    }
}

2.3 ArrayList

2.3.1 ArrayList底层原理

复制代码
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

2.3.2 LinkedList集合的底层原理

复制代码
LinkedList
 底层数据结构:
     基于双向链表实现(内存地址不连续,每个元素记录自己的前后元素)
 特点:
        1. 查询速度慢
        2. 增删效率高
        3. 对于首尾元素进行增删改查的速度都是极快的
 应用场景:
        1. 用来设计队列(两端开口,类似于一个管道,先进先出)
         只操作首尾元素, 尾部添加, 首部删除
        2. 用来设计栈(一段开口,类似于弹夹,先进后出)
java 复制代码
public class Demo4 {
​
    public static void main(String[] args) {
        makeQueue();
        System.out.println("----------------");
        makeStack();
    }
​
    /*
        队列: 两端开口,特点是先进先出(排队)
        从队列后端入队列:  addLast 方法
        从队列前端出队列:  removeFirst方法
    */
    public static void makeQueue() {
​
        LinkedList<String> queue = new LinkedList<>();
​
        //从队列后端入队列: addLast方法
        queue.addLast("第1位顾客");
        queue.addLast("第2位顾客");
        queue.addLast("第3位顾客");
        queue.addLast("第4位顾客");
        System.out.println(queue);
​
        //从队列前端出队列: removeFirst方法
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);
    }
运行结果:
    [第1位顾客, 第2位顾客, 第3位顾客, 第4位顾客]
    第1位顾客
    第2位顾客
    [第3位顾客, 第4位顾客]
​
    /*
        栈: 顶端开口的结构,特点是先进后出
        进栈/压栈: push方法(底层封装了addFirst 方法)
        出栈/弹栈: pop方法底 (底层封装了removeFirst方法)
    */
    public static void makeStack() {
        LinkedList<String> stack = new LinkedList<>();
​
        //进栈/压栈: push方法(底层封装了addFirst 方法)
        stack.push("第1颗子弹");
        stack.push("第2颗子弹");
        stack.push("第3颗子弹");
        stack.push("第4颗子弹");
        System.out.println(stack);
​
        //出栈/弹栈: pop方法底(底层封装了removeFirst方法)
        System.out.println(stack.pop());
        System.out.println(stack.pop());
        System.out.println(stack);
    }
 运行结果:
    [第4颗子弹, 第3颗子弹, 第2颗子弹, 第1颗子弹]
    第4颗子弹
    第3颗子弹
    [第2颗子弹, 第1颗子弹]   
}

三、Set系列集合

1、Set系列集合的特点是啥?

无序、不重复

2、Set集合的常见实现类有哪几个?各自有啥特点?

HashSet :无序、不重复

LinkedHashSet: 有序、不重复

TreeSet :可排序、不重复

java 复制代码
public class Demo1 {
    public static void main(String[] args) {
        testHashSet();
        System.out.println("==========");
        testLinkedHashSet();
        System.out.println("==========");
        testTreeSet();
    }
​
    //HashSet: 无序、没有索引、不可重复
    private static void testHashSet() {
        HashSet<Integer> hashSet = new HashSet<>();
        hashSet.add(44);
        hashSet.add(33);
        hashSet.add(11);
        hashSet.add(22);
        hashSet.add(22);
        System.out.println(hashSet);
    }
​
    //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<Integer> treeSet = new TreeSet<>();
        treeSet.add(44);
        treeSet.add(33);
        treeSet.add(11);
        treeSet.add(22);
        treeSet.add(22);
        System.out.println(treeSet);
    }
}
​

3.1 HashSet

3.1.1 哈希值

复制代码
哈希值
    就是一int值,Java每个对象都可以通过hashCode方法,获取自己的哈希值
    
    public int hashCode():返回对象的哈希码值。 
​
​
哈希值特点
    1.同一个对象多次调用hashCode方法,返回的哈希值是相同的;
    2.不同的对象,他们哈希值大几率不相同,但是也有可能会相同(哈希碰撞)
    3.Object的hashCode方法根据"对象地址值"计算哈希值,子类重写后的hashCode方法可以根据"对象属性值"计算哈希值
​
使用场景
    HashSet集合判定两个对象的标准就是两个对象的hash值是否一致, 因此我们经常重写hashcode实现集合中对象去重
java 复制代码
public class Demo2 {
    public static void main(String[] args) {
        //创建一个学生对象,获取对象的哈希值
        //1.同一个对象多次调用hashCode方法,返回的哈希值是相同的;
        Student student = new Student("Tom", 15);
        int code = student.hashCode();
        System.out.println(code);
        int code1 = student.hashCode();
        System.out.println(code1);
​
        //2.不同的对象,他们哈希值大几率不相同,但是也有可能会相同(哈希碰撞)
        Student student1 = new Student("Jerry", 16);
        int code2 = student1.hashCode();
        System.out.println(code2);
        //3.Object的hashCode方法根据"对象地址值"计算哈希值
        //子类重写后的hashCode方法可以根据"对象属性值"计算哈希值
        Student student2 = new Student("Jerry", 16);
        int code3 = student2.hashCode();
        System.out.println(code3);
    }
}
​
class Student{
    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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    //重写hashCode方法,根据对象的属性值计算哈希值
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

3.1.2 Set集合去重

需求:

创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,要求:学生对象的成员变量值相同,我们就认为是同一个对象

分析

①定义学生类,创建HashSet集合对象, 创建学生对象

②把学生添加到集合

③在学生类中重写两个方法,hashCode()和equals(),自动生成即可

java 复制代码
public class Demo2 {
    public static void main(String[] args) {
    
        //通过HashSet集合,存储学生数据(去重:需要重写hashCode()和equals())
        HashSet<Student> students = new HashSet<>();
        //构造几个学生对象,存入set集合
        Student student1 = new Student("Tom", 15);
        Student student2 = new Student("Tom", 15);
        Student student3 = new Student("Tim", 19);
        students.add(student1);
        students.add(student2);
        students.add(student3);
        System.out.println(students);
    }
}
​
class Student{
    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 String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
​
    //重写hashCode方法,根据对象的属性值计算哈希值
​
    @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);
    }
​
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

3.1.3 HashSet集合的底层原理

  • 基于哈希表实现

  • 哈希表是一种增删改查数据,性能都较好的数据结构。

1、HashSet集合的底层原理是什么样的?

基于哈希表实现的。

JDK8之前的,哈希表:底层使用数组+链表组成

JDK8开始后,哈希表:底层采用数组+链表+红黑树组成。

2、HashSet集合利用哈希表操作数据的详细流程是咋回事?

① 创建一个默认长度16,默认加载因为0.75的数组,数组名table

② 根据元素的哈希值跟数组的长度计算出应存入的位置

③ 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,

则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组.

④ 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的1倍

3.2 LinkedHashSet

3.3 TreeSet

复制代码
TreeSet 可排序(默认升序排序 ,按照元素的大小,由小到大排序)、不重复、无索引
 底层基于红黑树实现排序,排序规则认为属性是相同的对象则不存
​
TreeSet的排序
 对于数值型Integer、Double,默认按照数值升序排列;
 对于String类型数据,默认按照字典排序
 
 对于自定义类,默认是无法排序的,需要我们指定排序规则
     1.自然排序:自定义类实现Comparable接口,重写compareTo方法,指定排序规则
     2.比较器排序:写在TreeSet构造参数中传递Comparator比较器对象,重写compare方法,指定排序规则
​
​

需求 使用TreeSet存储教师对象,重复对象不存,并且用两种方式按照年龄升序排列

javascript 复制代码
//比较器排序:写在TreeSet构造参数中传递Comparator比较器对象,重写compare方法,指定排序规则
public class Demo4 {
​
    public static void main(String[] args) {
        //创建TreeSet
        TreeSet<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 +
                '}';
    }
​
}
运行结果:
    Teacher{name='赵六', age=17}
    Teacher{name='李四', age=18}
    Teacher{name='张三', age=19}
    Teacher{name='王五', age=20}
java 复制代码
//自然排序:自定义类实现Comparable接口,重写compareTo方法,指定排序规则
public class Demo5 {
    public static void main(String[] args) {
        //创建TreeSet
        TreeSet<Teacher1> treeSet = new TreeSet<>();

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

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

class Teacher1 implements Comparable<Teacher1>{
    private String name;
    private int age;

    public Teacher1() {
    }

    public Teacher1(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 int compareTo(Teacher1 o) {
        return this.age - o.age;
    }
}
运行结果:
	Teacher{name='赵六', age=17}
	Teacher{name='李四', age=18}
	Teacher{name='张三', age=19}
	Teacher{name='王五', age=20}

四、总结

相关推荐
皓木.几秒前
苍穹外卖——准备工作
java·数据库·mybatis
愤怒的代码14 分钟前
Spring Boot对访问密钥加密解密——RSA
java·spring boot·后端
美美的海顿15 分钟前
springboot基于Java的校园导航微信小程序的设计与实现
java·数据库·spring boot·后端·spring·微信小程序·毕业设计
愤怒的代码17 分钟前
Spring Boot中幂等性的应用
java·spring boot·后端
silver68718 分钟前
JAVA8 Stream API 使用详解
java
武子康20 分钟前
大数据-259 离线数仓 - Griffin架构 修改配置 pom.xml sparkProperties 编译启动
xml·java·大数据·hive·hadoop·架构
IT 古月方源31 分钟前
ensp 关于ARRP 的讲解 配置
运维·开发语言·网络·tcp/ip·智能路由器
西猫雷婶36 分钟前
python学opencv|读取图像(二十二)使用cv2.polylines()绘制多边形
开发语言·python·opencv
温轻舟39 分钟前
前端开发 -- 自定义鼠标指针样式
开发语言·前端·javascript·css·html·温轻舟
xiaocaibao7771 小时前
编程语言的软件工程
开发语言·后端·golang