java基础-12 : 单列集合(Collection)

一.单列集合体系结构

  • List系列集合:添加的元素时有序,可重复,有索引
  • Set系列集合:添加的元素时无序,不重复,无索引

二.单列集合的通用方法

1.1基本数据类型的用法

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.Collection;

public class demo1 {
    public static void main(String[] args) {
        // 1. 使用多态方式创建集合
        Collection<String> list = new ArrayList<>();
        // 输出空列表:[]
        System.out.println(list);
        // 2.添加元素,添加成功返回true
        boolean result1 = list.add("1");
        boolean result2 = list.add("2");
        boolean result3 = list.add("3");
        System.out.println(result1); // true
        System.out.println(list);
        // 3.删除元素,删除成功返回true
        list.remove("2");
        System.out.println(list);
        // 4. 集合长度
        System.out.println(list.size()); // 2
        // 5. 集合是否为空
        System.out.println(list.isEmpty()); // false
        // 6. 集合是否包含某个元素
        System.out.println(list.contains("1")); // true
    }
}

1.2引用数据类型的用法

java 复制代码
public static void main(String[] args) {
        ArrayList<Student> list = new ArrayList<Student>();
        Student s1 = new Student("张三", 12);
        Student s2 = new Student("李四", 13);
        Student s3 = new Student("王五", 14);
        list.add(s1);
        list.add(s2);
        list.add(s3);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i).getName() + "," + list.get(i).getAge());
        }
        for (int i = 0; i < 3; i++) {
            Student s = new Student();
            Scanner sc = new Scanner(System.in);
            System.out.println("输入学生姓名:");
            String name = sc.nextLine();
            System.out.println("输入学生年龄");
            int age = sc.nextInt();

            s.setName(name);
            s.setAge(age);
            list.add(s);
        }
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i).getName() + "," + list.get(i).getAge());
        }
    }

2.遍历集合

1.迭代器

先获取数据,再移动指针,遍历后指针不复位,既依然指向最后没有数据的位置,删除时只能使用迭代器的remove方法,不能使用集合的remove方法

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.function.Consumer;


public class demo1 {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        Iterator<String> it1 = list.iterator();
        while(it1.hasNext()){
            String s = it1.next();
            System.out.println(s);
        }
        Iterator<String> it2 = list.iterator();
        while(it2.hasNext()){
            String s = it2.next();
            if("b".equals(s)){
                it2.remove();
            }
        }
        System.out.println(list);
    }
}
java 复制代码
a
b
c
[a, c]

2.增强for

增强for: 内部原理是迭代器,只能遍历单列集合,数组,不能遍历Set双列集合

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.Collection;


public class demo1 {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        for (String s : list) {
            //s = "d";//不能修改集合中的元素
            System.out.println(s);
        }
    }
}
java 复制代码
a
b
c

3.使用forEach+lambda

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;


public class demo1 {
    public static void main(String[] args) {
        Collection<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        list.forEach((String s) -> System.out.println(s));
        //等同于:list.forEach(s -> System.out.println(s));
    }
}
java 复制代码
a
b
c
a
b
c

三.集合list的特有方法

特性 ArrayList LinkedList
底层数据结构 动态数组 双向链表
访问元素 极快,通过索引直接访问,时间复杂度 O(1) ,需要从头或尾遍历,时间复杂度 O(n)
插入/删除元素 ,需要移动后续元素,时间复杂度 O(n) ,只需修改指针,时间复杂度 O(1)
内存开销 较小,只存储数据和数组容量 较大,每个节点都需要存储数据和两个指针
适用场景 频繁随机访问,多读少写 频繁在头尾或中间插入/删除,少随机访问

ArrayListLinkedList 都实现了 List<E> 接口。这意味着它们对外承诺了完全相同的一组方法。因此,从代码编写的角度来看,你可以用完全相同的方式去调用 add(int index, E element), remove(int index), set(int index, E element), get(int index) 这些方法,而不需要关心底层是数组还是链表.

有序可重复

  1. 有序: 元素有索引,可以根据索引操作元素
  2. 可重复: 集合中可以存储重复元素

1.void add(int index ,E element)

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        System.out.println(list);
        list.add(1,5);
        System.out.println(list);
    }
}
java 复制代码
[3, 2, 1]
[3, 5, 2, 1]

2.remove(int index)/remove(Object O)

  • remove(int index): 删除指定索引处的元素,返回被删除的元素
  • remove(Object o): 删除指定元素,返回是否删除成功
java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        System.out.println(list);
        list.remove(Integer.valueOf(2));
        System.out.println(list);
        list.remove(1);
        System.out.println(list);
    }
}
java 复制代码
[3, 2, 1]
[3, 1]
[3]

因为在调用方法时,如果出现了重载的现象,优先调用实参跟形参类型一致的方法(缺少了封箱这一步)

3.set(int index,E element)

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        System.out.println(list);
        list.set(0, 100);
        System.out.println(list);
    }
}
java 复制代码
[3, 2, 1]
[100, 2, 1]

4.get(int index)

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        System.out.println(list.get(1));
    }
}
java 复制代码
2

5.列表遍历

1.普通for循环与增强for循环

因为list是有序的,所以可以使用索引

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        for (Integer i : list) {
            System.out.println(i);
        }
        System.out.println("-----------------");
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}
java 复制代码
3
2
1
-----------------
3
2
1

2.列表迭代器

java 复制代码
package 集合;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class demo1 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        ListIterator<Integer> listit = list.listIterator();
        while(listit.hasNext()){
            Integer i = listit.next();
            if(i == 2){
                listit.add(521);
            }
            System.out.println(i);
        }
        System.out.println(list);
    }
}
java 复制代码
3
2
1
[3, 2, 521, 1]

对比总结

特性维度 迭代器遍历 增强for循环 普通for循环 列表迭代器 forEach遍历
删除能力 ✅ 安全删除 ❌ 不支持 ⚠️ 需手动调整索引 ✅ 安全删除 ❌ 不支持
添加能力 ❌ 不支持 ❌ 不支持 ⚠️ 需手动调整索引 ✅ 支持添加 ❌ 不支持
修改能力 ❌ 不支持 ❌ 不支持 ✅ 直接修改 ✅ 支持修改 ❌ 不支持
双向遍历 ❌ 单向 ❌ 单向 ⚠️ 可反向但复杂 ✅ 双向 ❌ 单向
适用集合 所有Collection 数组+Iterable 仅List 仅List 所有Iterable
  • 迭代器循环:在遍历过程中需要删除元素
  • 增强for循环:仅仅遍历
  • forEach:仅仅遍历
  • 普通for循环:需要对索引进行操作
  • 使用列表迭代器:在遍历过程中需要添加元素

四.底层原理

1.ArrayList

  1. 利用空参构造创建的集合,在底层创建一个默认长度为0的数组
  2. 在添加第一个元素时,底层会创建一个新的长度为10的数组
  3. 存满时,会扩容1.5倍
  4. 如果一次性添加多个元素,1.5倍放不下,则新创建的数组长度以实际为准

2.LinkedList

3.iterator

五.集合Set

特性 HashSet TreeSet
底层数据结构 基于 HashMap (哈希表) 基于 TreeMap (红黑树)
元素顺序 无序(不保证插入顺序或任何特定顺序) 有序(按元素的自然顺序或自定义比较器排序)
性能 (增、删、查) O(1) 平均时间复杂度 O(log n) 平均时间复杂度
元素要求 必须正确实现 equals()hashCode() 方法 必须实现 Comparable 接口 提供 Comparator
允许 null 元素 允许 一个 null 元素 不允许 (如果使用自然排序,因为 null 无法比较)
线程安全 非线程安全 非线程安全

1.HashSet

hashCodeequals

  • 当添加元素时,HashSet 会调用元素的 hashCode() 方法来确定存储位置。

  • 如果两个元素的 hashCode 相同,它会再调用 equals() 方法来检查它们是否真的相等。

  • 如果两个对象通过 equals() 比较是相等的,那么它们的 hashCode() 必须返回相同的值。 否则,会导致重复元素被加入,破坏了 Set 的唯一性。

在使用引用数据类型时,需要重写hashCode()和equals()方法

String 与 integer 类型都重写了hashCode()和equals()方法

java 复制代码
import java.util.Objects;

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 String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

}
java 复制代码
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 18);
        Student s2 = new Student("张三", 18);
        Student s3 = new Student("李四", 19);
        Student s4 = new Student("王五", 20);
        Set<Student> set = new HashSet<Student>();
        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(s4);
        System.out.println(set);
    }
}
java 复制代码
[Student{name = 张三, age = 18}, Student{name = 王五, age = 20}, Student{name = 李四, age = 19}]

2.TreeSet

TreeSet 是基于红黑树(一种自平衡的二叉搜索树)实现的 Set,它保证了元素处于排序状态。

元素必须可比较

  • 自然排序 :元素类必须实现 java.lang.Comparable 接口,并重写 compareTo 方法。

  • 定制排序 :在创建 TreeSet 时,传入一个 Comparator 比较器对象。

1.实现Comparable接口,并重写 compareTo方法

java 复制代码
package 集合;

import java.util.Objects;

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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student o) {
//        this 表示当前要填加的元素, o 表示在红黑树存在的元素
        return this.age - o.age;
    }
}
java 复制代码
package 集合;

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class test {
    public static void main(String[] args) {
        Student s1 = new Student("张三", 18);
        Student s2 = new Student("张三", 18);
        Student s3 = new Student("李四", 19);
        Student s4 = new Student("王五", 20);
        Set<Student> set2 = new TreeSet<Student>();
        set2.add(s1);
        set2.add(s2);
        set2.add(s3);
        set2.add(s4);
        System.out.println(set2);
    }
}
java 复制代码
[Student{name = 张三, age = 18}, Student{name = 李四, age = 19}, Student{name = 王五, age = 20}]

2.使用比较器

java 复制代码
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class test2 {
    public static void main(String[] args) {
//        使用比较器进行排序规则的指定
//        规则当长度相同时,按照字典序排序,长度不同时,按照长度排序
        Set<String> set = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
//                o1 表示当前要填加的元素, o2 表示在红黑树存在的元素
                int i = o1.length() - o2.length();
                i = i==0? o1.compareTo(o2) : i;
                return i;
            }
        });
        set.add("a");
        set.add("bw");
        set.add("qwer");
        set.add("s");
        System.out.println(set);
    }
}
java 复制代码
[a, s, bw, qwer]

六.泛型

1.泛型类

在集合中不限制子类类型时,会默认为Object类型,可以往集合中添加任意类型的数据,类似于多态形式,但是不可以调用子类的特性

java 复制代码
// 定义一个泛型类 Box
public class Box<T> {
    private T content; // T 代表一个未知的类型

    public void setContent(T content) {
        this.content = content;
    }

    public T getContent() {
        return content; // 返回类型是 T,不需要强制转换
    }
}

在创建类时,使用形参E进行表示,在创建实例对象时,才给出数据类型

java 复制代码
import java.util.Arrays;
//1.泛型类
public class myArrayList<E>{
    Object[] obj = new Object[10];
    int size = 0;
//    添加元素
    public boolean add(E e){
        obj[size] = e;
        size++;
        return true;
    }
    public E get(int index){
        return (E)obj[index];
    }
    //    重写toString方法
    @Override
    public String toString(){
        return Arrays.toString(obj);
    }
}
java 复制代码
import java.util.ArrayList;

public class demo1 {
    public static void main(String[] args) {
//        1.泛型类
        myArrayList<String> list = new myArrayList<>();
        list.add("hello");
        list.add("world");
        System.out.println(list);
java 复制代码
[hello, world, null, null, null, null, null, null, null, null]

2.泛型方法

java 复制代码
public class Utility {
    
    // 一个普通的泛型方法(在非泛型类中)
    public static <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.print(element + " ");
        }
        System.out.println();
    }

    // 另一个泛型方法,有两个类型参数
    public static <T, U> void printPair(T first, U second) {
        System.out.println("First: " + first + ", Second: " + second);
    }
}
java 复制代码
public class Main {
    public static void main(String[] args) {
        Integer[] intArray = {1, 2, 3};
        String[] strArray = {"A", "B", "C"};

        // 调用泛型方法。通常编译器可以推断出类型 T,无需显式指定
        Utility.printArray(intArray); // T 被推断为 Integer
        Utility.printArray(strArray); // T 被推断为 String

        // 显式指定类型(不常用,通常在类型推断失败时使用)
        Utility.<Integer>printArray(intArray);

        Utility.printPair("Age", 25); // T 是 String, U 是 Integer
    }
}

3.泛型接口

1.实现类给出具体类型

java 复制代码
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

//3.泛型接口
public class list implements List<String> {
    ...
    @Override
    public boolean add(String s) {
        return false;
    }
    ...
}
java 复制代码
list list2 = new list();
list2.add("hello");
list2.add("world");
System.out.println(list2);

2.实现类延续泛型,在创建实现类对象时再确定类型

java 复制代码
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;


public class list2<E> implements List<E> {
    ...
    @Override
    public boolean add(E e) {
        return false;
    }
    ...
}
java 复制代码
list2<String> list3 = new list2<>();
list3.add("hello");
list3.add("world");
System.out.println(list3);

4.通配符

当方法中不确定使用什么类型的泛型时,使用通配符?

  • ? extends E 表示泛型类型是E的子类
  • ? super E 表示泛型类型是E的父类
  • ? 表示任意类型
相关推荐
whltaoin2 小时前
Spring Boot自定义全局异常处理:从痛点到优雅实现
java·spring boot·后端
sun03222 小时前
工作中使用到的单词(软件开发)_第五版
开发语言·软件开放单词
zhangxuyu11182 小时前
Spring boot 学习记录
java·spring boot·学习
做运维的阿瑞2 小时前
告别性能焦虑:Python 性能革命实践指南
开发语言·后端·python
元气满满的霄霄2 小时前
Spring Boot整合缓存——Ehcache缓存!超详细!
java·spring boot·后端·缓存·intellij-idea
MClink2 小时前
架构学习之旅-架构的由来
java·学习·架构
-雷阵雨-3 小时前
数据结构——排序算法全解析(入门到精通)
java·开发语言·数据结构·排序算法·intellij-idea
eqwaak03 小时前
科技信息差(10.2)
开发语言·python·科技·科技信息差
aloha_7893 小时前
顺丰科技java面经准备
java·开发语言·spring boot·科技·spring·spring cloud