Collection集合

  • Post author:
  • Post category:其他





概念:

集合是java中提供的一种容器,可以用来存储多个数据




集合和数组的区别:

  • 数组的长度是固定的,集合的长度是可变的。
  • 数组中存储的是同一类型的元素,可以存储任意类型数据。集合存储的都是引用数据类型。如果想存储基本类型数据需要存储对应的包装类型。集合的底层也是数组。
  • 集合底层是数组,在创建集合的时,会创建一个长度为10的数组,如果长度不够就会自动扩容原来数组长度的1.5倍,也就是15,然后将原数组拷贝的数据拷贝到长度15的数组里。



集合的体系结构:

请添加图片描述




Collection:

是单列集合的顶层接口,JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现


创建对象方式:

多态的方式

具体的实现类ArrayList




Collection常用方法:

方法名 说明
boolean add(E e) 添加元素
boolean remove(Object o) 从集合中移除指定的元素
boolean removeIf(Object o) 根据条件进行移除
void clear() 清空集合中的元素
boolean contains(Object o) 判断集合中是否存在指定的元素
boolean isEmpty() 判断集合是否为空
int size() 集合的长度,也就是集合中元素的个数
    public static void main(String[] args) {
        Collection<String> arrayList = new ArrayList<>();
        // 添加
        arrayList.add("itzhuzhu");
        arrayList.add("hehe");
        arrayList.add("今天过生日");

        // 删除
        System.out.println(arrayList.remove("itzhuzhu"));
        System.out.println(arrayList.remove("这里删除一段不存在的看一下返回的结果"));

        // 根据条件删除
        // removeIf底层会遍历集合,s代表每个元素,true删除,false不删除
        arrayList.removeIf((String s) -> {
            return s.length() == 4;
        });

        // 清空集合所有元素
        arrayList.clear();

        // 判断集合中是否存在指定的元素
        System.out.println("contains: " + arrayList.contains("hehe"));

        // 判断集合是否为空
        System.out.println("isEmpty: " + arrayList.isEmpty());

        // 返回集合的长度
        System.out.println("arrayList:" + arrayList.size());
        System.out.println(arrayList);
    }



iterator:

  • iterator是迭代器,集合专属遍历方式
  • 迭代器原理,默认指向0索引,依次按照需求操作,但是每次都会先–,再操作,对比fori循环先++,可以解决相同邻近元素无法删除操作。

循环执行:

  1. 先根据当前位置判断有没有下一个
  2. 获取当前位置的元素,再指向下一个

为什么要用迭代器:

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口 java.util.Iterator 。

迭代器怎么获取:

 Iterator 对象 = 集合对象.iterator()

迭代器的实现原理

  • 当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
  • Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。

在这里插入图片描述


常用方法:

方法名 说明
Iterator iterator() 创建迭代器,默认指向0索引​
boolean hasNext() 判断当前位置是否有元素可以被取出
​E next() 获取当前位置的元素,将迭代器对象移向下一个索引位置
void remove() 删除迭代器对象当前指向的元素
    public static void main(String[] args) {
        //创建集合对象
        Collection<String> arrayList = new ArrayList<>();

        //添加元素
        arrayList.add("hello");
        arrayList.add("world");
        arrayList.add("world");

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

        while (iterator.hasNext()) {
            String next = iterator.next();
            if ("world".equals(next)) {
        /**  不使用remove要手动去判断,当两个相邻的元素一样就会删除不了,因为循环判断以后就会做++,
             集合会自动加减长度,假如索引1和2都是a,那么删除了索引1,集合就会把索引2的a挪到1
        */
                iterator.remove();
            }
            System.out.println("next = " + next);
        }
        System.out.println("arrayList = " + arrayList);
    }




增强for :

  • 增强for是JDK5之后出现的,其内部原理是一个Iterator迭代器
  • 实现Iterable接口的类才可以使用迭代器和增强for,只要单列集合才可以使用,双列的都不可以。
  • 简化数组和Collection集合的遍历


格式:

for(集合/数组中元素的数据类型 变量名 :  集合/数组名) {// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可}
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        list.add("f");

        for (String str : list) {
            // 在集合里修改第三方变量值可以实现,但是不会影响集合原来的值
            str = "hehe";
            System.out.println("str = " + str);
        }
        System.out.println("list = " + list);
    }



三种循环的使用场景:

  1. 需要操作索引就用普通for循环
  2. 只需要遍历使用增强for
  3. 遍历的过程中需要删除元素,用迭代器


代码演示:

 public static void main(String[] args) {
        // 我们学习Collection集合中的方法,编译看左边,我们使用的方法都是来自Collection接口
        // 多态
        // 接口            变量名 = new 实现类();
        Collection<String> coll = new ArrayList<>();
        // 1.boolean add​(E e) 往集合中添加一个元素
        coll.add("王宝强");
        coll.add("谢霆锋");
        coll.add("贾乃亮");
        coll.add("陈羽凡");
        System.out.println(coll);
        // 2.void clear​() 从此集合中删除所有元素
        // coll.clear();

        // 3.boolean contains​(Object o) 判断集合中是否包含指定的元素
        System.out.println(coll.contains("贾乃亮")); // true
        System.out.println(coll.contains("杜海涛")); // false

        // 4.boolean isEmpty​() 判断集合是否为空(没有元素),如果为空返回true
        System.out.println(coll.isEmpty());

        // 5.boolean remove​(Object o) 删除集合中的指定元素,如果删除返回true
        System.out.println(coll.remove("王宝强")); // true
        System.out.println(coll.remove("大郎")); // false

        // 6.int size​() 返回此集合中的元素数
        System.out.println(coll.size());
        System.out.println(coll);
        System.out.println("------");
        // 7.Object[] toArray​() 将集合转成数组
        Object[] objects = coll.toArray();
        for (int i = 0; i < objects.length; i++) {
            Object obj = objects[i];
            System.out.println(obj);
        }
    }



List集合:

有序集合,这里的有序指的是存取顺序

可以精确控制列表中每个元素的插入位置,可以通过整数索引访问元素,并搜索列表中的元素,与Set集合不同,列表通常允许重复的元素

常用的子类:

  1. ArrayList
  2. LinkedList


特点:

  1. 存取有序
  2. 可以重复
  3. 有索引



List集合的特有功能

方法名 描述
void add(int index,E element) 在此集合中的指定位置插入指定的元素
E remove(int index) 删除指定索引处的元素,返回被删除的元素
E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
E get(int index) 返回指定索引处的元素


代码演示:

    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();

        // void add(int index,E element)	在此集合中的指定位置插入指定的元素
        list.add(0,"a");
        list.add("b");
        list.add("c");
        list.add("a");

        // E remove(int index)	删除指定索引处的元素,返回被删除的元素
        list.remove(0);
        list.remove("b");
        System.out.println("list = " + list);

        // E set(int index,E element)	修改指定索引处的元素,返回被修改的元素
        list.set(1, "修改了索引1");

        // E get(int index)	返回指定索引处的元素
        System.out.println("索引1 = " + list.get(1));
        System.out.println("list = " + list);
    }



ArrayList:

底层是数组结构实现,查询快、增删慢




LinkedList集合的特有功能:

底层是双向链表结构实现,查询慢、增删快

方法名 说明
public void addFirst(E e) 在该列表开头插入指定的元素
public void addLast(E e) 将指定的元素追加到此列表的末尾
public E getFirst() 返回此列表中的第一个元素
public E getLast() 返回此列表中的最后一个元素
public E removeFirst() 从此列表中删除并返回第一个元素
public E removeLast() 从此列表中删除并返回最后一个元素
    public static void main(String[] args) {
        LinkedList<String> list = new LinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");

        // public void addFirst(E e)	在该列表开头插入指定的元素
        list.addFirst("-a");

        // public void addLast(E e)	将指定的元素追加到此列表的末尾
        list.addLast("d");
        System.out.println("操作前:" + list);

        // public E getFirst()	返回此列表中的第一个元素
        System.out.println("第一个元素 :" + list.getFirst());

        // public E getLast()	返回此列表中的最后一个元素
        System.out.println("最后一个元素 : " + list.getLast());

        // public E removeFirst()	从此列表中删除并返回第一个元素
        System.out.println("删除第一个元素 : " + list.removeFirst());

        // public E removeLast()	从此列表中删除并返回最后一个元素
        System.out.println("删除最后一个元素() : " + list.removeLast());
        System.out.println("操作后 : " + list);
    }



Set集合:

  1. 不可重复
  2. 存取无序
  3. 无索引不能使用普通for循环遍历



TreeSet:

  1. 不可以存储重复元素
  2. 没有索引
  3. 可以将元素按照规则进行排序

    (使用TreeSet必须定义排序规则)
  4. TreeSet底层实现的是红黑树的数据结构


实现原理:


在这里插入图片描述




自然排序Comparable:

Comparator是一个比较接口,需求需要实现比较就要实现该接口,并重写compareTo方法


实现步骤:

  1. 使用空参构造创建TreeSet集合

    用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
  2. 自定义类实现Comparable接口

    自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
  3. 重写接口中的compareTo方法

    重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写


返回值规则:

  1. 如果返回值为负数,表示当前存入的元素是较小值,存左边
  2. 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
  3. 如果返回值为正数,表示当前存入的元素是较大值,存右边
  4. 排序在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序,比如要按照字符串排序就可以用compare调用length排序


compareTo:

  • int compareTo​(T o):将此对象与指定的对象进行比较以获得顺序。 返回负整数,零或正整数,因为此对象小于,等于或大于指定对象。
  • 用于字符串与对象进行比较
  • 按字典顺序比较两个字符串


代码演示:


学生类

public class Student implements Comparable<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 +
                '}';
    }

    @Override
    public int compareTo(Student o) {
        // 主要判断条件:按照年龄排序
        int result = this.age - o.age;
        //次要判断条件: 年龄相同时,按照姓名的字母顺序排序,这里的compareTo是String类的
        result = result == 0 ? this.name.compareTo(o.getName()) : result;
        return result;
    }
}

测试类

public class Demo {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<Student> ts = new TreeSet<>();
        // 存入学生
        Student s1 = new Student("zhangsan", 28);
        Student s2 = new Student("lisi", 27);
        Student s3 = new Student("wangwu", 29);
        Student s4 = new Student("zhaoliu", 28);
        Student s5 = new Student("qianqi", 30);
        // 添加学生到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        // 遍历集合
        for (Student student : ts) {
            System.out.println(student);
        }
    }
}



比较器排序comparator:

此比较器可以比较的对象类型,Comparator最常用的场景就是排序和分组


使用步骤:

创建TreeSet对象的时候传递comparator的实现类对象,重写compare方法,根据返回值进行排序


返回值规则:

  1. 如果返回值为负数,表示当前存入的元素是较小值,存左边
  2. 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
  3. 如果返回值为正数,表示当前存入的元素是较大值,存右边
  4. 排序在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序,比如要按照字符串排序就可以用compare调用length排序


代码演示:


老师类

public 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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

测试类

public class TeacherTest {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {

            @Override
            public int compare(Teacher o1, Teacher o2) {
                //o1表示现在要存入的那个元素 o2表示已经存入到集合中的元素
                //主要条件
                int result = o1.getAge() - o2.getAge();
                //次要条件
                result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
                return result;
            }
        });
        // 存入老师
        Teacher s1 = new Teacher("zhangsan", 28);
        Teacher s2 = new Teacher("lisi", 27);
        Teacher s3 = new Teacher("wangwu", 29);
        Teacher s4 = new Teacher("zhaoliu", 28);
        Teacher s5 = new Teacher("qianqi", 30);

        // 添加老师到集合
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);
        // 遍历集合
        for (Teacher teacher : ts) {
            System.out.println(teacher);
        }
    }
}


排序总结:

排序的时候想好主要条件和次要提交条件,这样做比较严谨




HashSet:

  1. 底层数据结构是哈希表
  2. 存取无序
  3. 不可以存储重复元素
  4. 没有索引,不能使用普通for循环遍历
  5. 属于Set集合所以元素唯一
  6. 底层是哈希表结构的



哈希值:

哈希值是JDK根据对象的

地址

或者

属性值

,算出来的int类型的

数值


获取哈希值:

Object类中的 public int hashCode():返回对象的哈希码值


哈希值的特点:

  1. 如果没有重写hashCode方法,那么是根据对象的地址值计算出的哈希值。

    • 同一个对象多次调用hashCode()方法返回的哈希值是相同的
    • 不同的对象的哈希值是不同的
  2. 如果重写hashCode方法,一般是通过对象的属性值计算出哈希值

    • 如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的



哈希表结构:


JDK1.8以前底层是数组+链表:

  1. 创建一个默认长度为16,加载因子(决定哈希表什么扩容)为0.75的数组,数组名为table
  2. 根据元素的哈希值跟数组的长度计算出应存入的位置
  3. 判断当前位置是否为null,如果是null直接存入
  4. 如果应存入的位置不为null,表示有元素,则调用equals方法比较属性值
  5. 如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面

请添加图片描述


JDK1.8以后是数组+链表+红黑树:

节点个数少于等于8个就用(数组 + 链表),多于8个用(数组 + 红黑树)

请添加图片描述


练习:

创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法

学生类:

public 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 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);
    }
}

测试类:

public class StudentTest {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<Student> hs = new HashSet<Student>();

        //创建学生对象
        Student s1 = new Student("韩信", 30);
        Student s2 = new Student("李白", 35);
        Student s3 = new Student("露娜", 33);
        Student s4 = new Student("露娜", 33);

        //把学生添加到集合
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);

        //遍历集合(增强for)
        for (Student s : hs) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}



版权声明:本文为weixin_45477086原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。