前言

很扯了,刚写完前言就结束了,我还没写美。平时也不怎么写博客,这是没被同质化拷打啊。但是感觉没那么有思考的知识可能没必要写🤔。那就写吧,突然学了Java的这些个集合,脑子都混了,不知道哪些集合该用到哪,什么时候该写这个Comparable或者什么时候写Comparator,我尽量写,如果今天写不完就分两篇写,我尽量哈⛽️

正文

写的范围

算了,这次就从这个单列集合开始吧,这里就介绍一下List和Set和这个Collection吧,下一篇来介绍这个Map以及这些集合的一些原理和集合的这个工具类Collections。

Collection

这是什么?

这个是一个接口,是那些Set,List这些单列集合的祖宗,这些集合都继承它,在java.util包里面
—那问题就来了:这个和那个Object什么关系?
——那当然是父子关系来,Object是所有类和所有接口的祖宗,那肯定Collection就是它后代,这是第一个可能会比较迷的东西(反正我迷了)

它有哪些方法?

  1. add方法
    这里可以直接添加一个元素(这里是一个泛型对象e,就是你Collection后面写的泛型,这里是String),返回值是一个boolean,如果添加成功就返回true,添加失败返回false
    上代码!!!
    注意
    1.这里的Collection是一个接口,无法实例化对象,所以这里接收这里ArrayList的对象
    2. 集合可以直接打印,这里底层都写好了格式
    Collection<String> collection = new ArrayList<>();
    //这里接收一下返回值(平时不接收)
    boolean b = collection.add("a");
    //打印返回值
    System.out.println(b);
    System.out.println(collection); 
    
  2. clear方法
    返回值和参数都是void,作用是清空集合的所有元素
    collection.clear();
    System.out.println(collection); 
    
  3. remove方法
    这里属于见名知义了,这里的意思就是删除,不过这个删除的参数是一个Collection的泛型里面的元素(后面有参数是下标的),返回值是一个布尔类型的,这里删除成功就返回true
    collection.add("b");
    collection.add("c");
    collection.remove("b");
    System.out.println(collection);
    
  4. contains方法
    这里是判断集合里面是否有这个对象,这个挺重要的,因为后面的if语句条件很可能就是它,里面传一个Object的对象,返回值是布尔类型的,如果存在就返回true

这里我刚刚出来了个问题——就是这里为什么有参数是泛型有参数是Object,后来我才知道为啥,这里的泛型是为了限制你操作的类型(这里要和集合定义的时候泛型传递的类型一致),而这里的Object对象才是可以传递任意类型可以让它判断一下是不是一致的

  1. isEmpty方法/size方法
    这里我直接一笔带过了,isEmpty返回boolean类型,如果空的就返回true,然后这里size返回int类型数字
    if(collection.isEmpty()){
       System.out.println("集合为空");
    }
    else{
        System.out.println("集合不为空");
        System.out.println(collection.size());
    }
    

遍历(比较重要)

这里的遍历确实比较重要,这里提供了三种遍历方式分别是

  1. 迭代器
  2. 增强for
  3. forEach(lambda表达式)
    我直接用代码演示了
迭代器

这里使用集合自带的iterator方法来创建迭代器对象,并且调用迭代器自己 的这个hasNext方法和next方法

  • 注意
    这里迭代器内部有一个索引,索引走到最后就无法遍历了,只能再创建迭代器进行遍历,当然这里List迭代器除外,不过它那个方法也是感觉一般般吧😂
//创建迭代器对象(注意一定要加上泛型,不然下面的元素都要强转,特别是后面Map的键值对,很麻烦)
Iterator<String>it = collection.iterator();
//hasNext()方法判断这个地方有没有元素,有则返回true,没有则返回false
 while(it.hasNext()){
     //迭代器的next()方法返回这个元素并且让索引指向下一个元素
     System.out.println(it.next());
 }
增强for

这里是JDK5之后出现的,底层就是这个迭代器,这个语法和cpp也是相似的
这里就像从这个collection里面一个个抽取这个s,然后打印这个s

for(String s : collection){
    System.out.println(s);
}
forEach(lambda)

本质是集合的forEach方法调用匿名内部类,这个匿名类调用Consumer接口,重载accept函数,打印这个s

collection.forEach(new Consumer<String>() {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
});
  • 注意
    然后我们就发现了这个Consumer是一个函数式接口,一旦牵扯到函数式编程,我们就可以用lambda表达式来简化代码了,这里就是简化规则
    我直接给上面的那个改个刀,开刀!!!
    1. new、继承的接口名、泛型、方法名、方法的修饰符、返回值、和形参类型可以省略,如果形参只有一个,可以省略包裹参数的括号,参数和方法体中间用->连接。
    //这里第一刀
    collection.forEach((String s) -> {
        System.out.println(s);
      }
    );
    
    1. 如果方法体内除了return的方法体只有一行,直接可以将方法体的括号和return和分号省略。但是要省略就一起省略
    //最终形态
    collection.forEach(s -> System.out.println(s));
    
总结

我比较建议使用这个增强for或者这个forEach方法了,特别是后面遍历Map的时候我感觉这个forEach非常牛逼了🐮

List

特点:有序、有索引、可重复

  1. 这里要提醒一下:刚刚Collection的那些个方法这个List集合都可以使用,毕竟是祖宗传下来的
  2. 这里List集合底下包含两种集合:ArrayList(动态数组)和LinkedList(链表),也是两种使用率比较高的数据结构了
  3. 不管是List还是ArrayList 或者LinkedList还是以后的什么Set和Map啊,这些个集合都在这个java.util包下,报错的时候可以看看有没有调用,这个以后就不提了

方法

Collection的方法它都能直接调用

  1. 构造方法
    这里直接使用ArrayList的集合创建ArrayList
    List<String> list = new ArrayList<>();
    
  2. add方法
    这里的特有add方法的返回值是void,这里的第一个参数是索引,第二个参数是泛型元素,这里会在你指定的索引处添加这个泛型元素
    List<String> list = new ArrayList<>();
      list.add("a");
      list.add("b");
      //这里就是这个方法
      list.add(0,"c");
      for(String s : list){
          System.out.println(s);
      }
    
  3. set方法
    这里的作用是修改,这里的返回值是一个泛型元素,其实返回的就是你修改前的元素第一个参数是索引,第二个参数是泛型元素
    ArrayList<String> list = new ArrayList<>();
    list.add("a");
    list.add("b");
    list.add(0,"c");
    list.set(0,"hehe");
    for(String s : list){
        System.out.println(s);
    }
    
  4. get方法
    这里的作用就是获取指定索引值的元素,这里的参数只有一个就是一个索引,这里返回一个泛型元素,这个方法也是我们可以普通for循环的底气,这里我直接在这个普通for循环来演示

遍历

这里除了Collection的三种遍历方式之后,这里还有另外两种遍历方式

普通for循环

因为有get方法,直接就可以for循环了

	for(int i = 0;i<list.size();i++)
	{
		System.out.print(list.get(i)+" ");
	}
列表迭代器

这个迭代器和之前那个迭代器的区别就是:原来的迭代器到了最后就不能用了,这个迭代器有一个方法可以从后往前遍历,让指针指向最前面,然后你再这个可以从前往后,而且它可以在遍历的时候增加修改或者删除元素,不过咱只说遍历

//从前往后
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
    System.out.println(it.next());
}
//从后往前
while(it.hasPrevious()){
  	System.out.println(it.previous());
}

写个List给我累够呛

ArrayList

这里的ArrayList的作用就是继承List,这个是可变数组,没有比较常见的特有方法,所以当List看就行

ArrayList<String> al = new ArrayList<>();

LinkedList

  1. 这里的LinkedList就很有说法了,因为它有很多的特有方法,除了特有方法,其他都可以直接用List的方法
  2. 底层是双链表,这里增删快改查慢

特有方法

  1. addFirst
    这里就是头插法,参数是一个泛型元素,返回值是void,一会直接一个代码给这些个方法都用了
  2. addLast
    参数和返回值和上面这个一样,这个是尾插法
  3. getFirst
    获取链表的头节点,这里参数void,返回值是一个泛型元素
  4. getLast
    获取尾节点,参数和上面这个一样
  5. removeFirst
    删除头节点,返回被删除的头节点元素
  6. removeLast
    删除尾节点,返回被删除的尾节点元素
     LinkedList<String> list = new LinkedList<>();
     list.addFirst("wang");
     list.addFirst("hi");
     list.addFirst("hehe");
     list.addLast("hello");
    
     System.out.println(list);
    
     System.out.println(list.getFirst());
     System.out.println(list.getLast());
    
     System.out.println(list.removeFirst());
     System.out.println(list.removeLast());
     
     System.out.println(list);
    

Set

特点:无序、无索引、不可重复
Set集合下面的集合
HashSet:哈希表(无序、无索引、不可重复)
LinkedHashSet:哈希表加链表(有序、无索引、不可重复)
TreeSet:红黑树(无序、无索引、不可重复)
注意这里的无序是指不按照添加的顺序

遍历

这里的遍历的方法和Collection集合遍历方法一样

HashSet

底层是哈希表,后面只要遇到哈希表就和这个hashcode和equals方法有关
这个集合有一个特点——这个集合在这个存自定义类的时候要重写hashcode和equals方法
这个hashcode和equals方法(因为哈希表的本质是这个通过哈希值找到对象)

如果是java自带的类型他都给你写好了,但是如果你是这个自定义类型,这里算哈希值的方法就是通过地址值来计算,但是我们想区分两个这个元素肯定不能用这个地址值啊,肯定是我们自己设定的属性值,所以这个时候我们就要重写这个hashcode和equals方法了

说实在的这里的这个hashcode和equals教的是直接用idea生成

//学生类
class Student implements Comparable<Student>
{
    private String name;
    private int age;
    //构造方法
    //无参构造方法
    //这里省略无参构造方法 
    //有参构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //getter方法
    //这里省略getter方法
    //setter方法
    //这里省略setter方法
    //重写toString方法
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //重写equals方法
    @Override
    public boolean equals(Object obj) {
        //这里判断this和obj是否为同一个对象
        //如果是同一个对象,返回true
        if (this == obj) {
            return true;
        }
        //这里判断obj是否为null
        //如果为null,返回false
        //getClass判断是不是一个类
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        //这里将obj转换为Student类型
        Student student = (Student) obj;
        //这里按照姓名和年龄比较
        return Objects.equals(name, student.name) && age == student.age;
    }
    //重写hashCode方法
    @Override
    public int hashCode() {
        //这里按照姓名和年龄计算hashCode值
        return Objects.hash(name, age);
    }
    //重写compareTo方法
    @Override
    public int compareTo(Student o) {
        //这里按照年龄比较
        return this.age - o.age;
    }
}

LinkedHashSet

这里其实和HashSet差不多,只是这里有一个双链表来记录这里这里添加的先后顺序

//创建LinkedHashSet对象
LinkedHashSet<Student> ls1 = new LinkedHashSet<>();

Student s1 = new Student("张三",18);
Student s2 = new Student("李四",19);
Student s3 = new Student("王五",20);
Student s4 = new Student("张三",18);

//添加学生对象到LinkedHashSet中
ls1.add(s1);
ls1.add(s3);
ls1.add(s2);
ls1.add(s4);

//这里是按照添加顺序打印的
System.out.println(ls1);

对比
这里你会发现这里HashSet会自己有一套排序顺序,而这里的LinkedHashSet会按照这里的添加顺序打印(但底层的存储方式还是HashSet一样的,只不过这里的链表保留了这个排列顺序)

HashSet<String> set = new HashSet<>();
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
set.add("wang");
set.add("hehe");
set.add("hi");
set.add("hello");
System.out.println(set);
linkedHashSet.add("wang");
linkedHashSet.add("hi");
linkedHashSet.add("hehe");
linkedHashSet.add("hello");
System.out.println(linkedHashSet);

TreeSet

这里底层是红黑树,总结这里只要牵扯到这个红黑树,这里就会和Comparable和这个Comparator扯上关系
既然是红黑树就扯到这个排序,既然是排序那就肯定要比大小

比较方法

  1. 数值类型就直接按照大小来比大小
  2. 如果是字符串类型就按照ASCii码值来比较大小
默认比较方法

调用时机:在定义类的时候。这里调用这个Comparable接口重载compareTo函数
注意:

  1. 这里第一个就是导包问题
  2. 第二个就是形参的逻辑关系,this是待加入的元素,这里的o是红黑树里的节点,所以这里的返回值—1.如果等于0,默认这里已经有这个节点了。2.如果大于0,说明这里的应该在节点右边。3.如果小于0,说明在节点左边
  3. 这里的升序和降序控制:如果o.age被减,说明是升序,如果o.age是减数,说明是降序(通过被减数和减数的关系来比节点大的放在左边还是右边)
    说明:如果是o.age被减,那就是大的放左边(小于0),如果是o.age做减数,那就是大的放右边(大于0)
//学生类
class Student implements Comparable<Student>
{
    //这里省略JavaBean类里面的方法和参数
    //重写compareTo方法
    @Override
    public int compareTo(Student o) {
        //这里按照年龄比较
        return this.age - o.age;
    }
}
比较器比较

这里就是这个在代码里面创建对象的时候调用了(匿名内部类+重载函数),这里的返回值和上面一样

//这里的逻辑就是先根据字符串长度排序,如果一样就按照这个ASCii值进行排序
//创建TreeSet对象
TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>() {
    @Override
    public int compare(String o1, String o2) {
         //这里o1是节点,o2是新元素
        if(o1.length()!=o2.length()){
            //这里根据长度进行排序(升序)
            return o1.length()-o2.length();
        }else
        {
            //这里根据字母顺序进行排序(如果长度一样)
            //这里的方法是字符串的比较方法
            return o1.compareTo(o2);
        }
    }
//添加字符串到TreeSet中
ts2.add("hello");
ts2.add("apple");
ts2.add("world");
ts2.add("java");
ts2.add("python");
System.out.println(ts2);

使用场景

这是黑马总结的使用场景
在这里插入图片描述

Collections工具类

这个工具类适用于单列集合(注意是单列集合!!!),提供了操控单列集合的一些方法,这里我会将这些方法一一列举出来。(注意到底是所有集合都可以使用还是只有List集合才能使用!!!)

常见方法

  1. addAll方法
    这个方法的特点就是可以批量添加元素,第一个参数是这个集合,第二个参数就是任意数量的这个泛型元素。
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi");
System.out.println(list);
  1. shuffle方法
    这个方法是打乱List集合的方法,比如我们接着上面直接写下面,这里返回值和这个参数都是void
Collections.shuffle(list);
System.out.println(list);
  1. sort方法
    这个方法是排序方法,这个方法有两个重载的版本
  • 默认版本(这里按照Java写好的方法进行排序)
    这里参数是List集合,这里字符串默认的排序是按照首字母进行排序
Collections.sort(list);
System.out.println(list);
  • 自定义版本
    这里多一个比较器参数对象,这里可以根据比较器的对象来进行自定义的一个排序
    这里的自定义排序方式是按照字符串长度升序排序
Collections.sort(list,new Comparator<String>() {
   @Override
     public int compare(String o1, String o2) {
         return o1.length()-o2.length();
     }
 });
 System.out.println(list);
  1. binarySearch方法
    这个方法的作用是查找,底层是二分查找。这里的第一个参数是一个有序List集合(这里一定要保证有序!!!),第二个参数是集合里面的值。
    这里的返回值很有意思,如果找到了就返回这里的索引,如果没找到就返回这里的-(插入点)-1
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");
Collections.sort(list);
System.out.println(list);
System.out.println(Collections.binarySearch(list, "wang"));
System.out.println(Collections.binarySearch(list, "hehe"));
System.out.println(Collections.binarySearch(list, "hi"));
System.out.println(Collections.binarySearch(list, "apple"));
  1. copy方法
    这里覆盖一个List集合到另一个List集合,第一个参数是目标集合,第二个参数是原始集合。这里要求目标集合必须有元素并且大于等于原始集合的数量,这样才可以覆盖
    依托答辩的方法啊!!!😠
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");


ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"zhangsan","lisi","wangwu","zhasan");
 Collections.copy(list2, list);
 System.out.println(list2);

这个才是正经的拷贝方法!!!

ArrayList<String> list2 = new ArrayList<>();
list2.addAll(list1);
  1. fill方法
    这也是个神人方法,功能是在一个**有元素的List集合里面,**把这个集合里面的元素换成你自己的元素,这里第一个参数是一个List集合,第二个参数是你要添加的元素(是一个Object对象)
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"wang","hehe","hi","apple");
Collections.fill(list, "hello");
System.out.println(list);
  1. max/min方法
    这里很正常的求一个集合(这次不是List集合了!!!)的最大/最小值,参数是一个集合,返回值是最大/最小元素本身
    默认版本
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,9,10,8,5,7,6,4,3,2);

System.out.println(Collections.max(list));
System.out.println(Collections.min(list));  

自定义比较器版本
自定义类型在定义类的时候调用Comparable接口或者在创建对象的时候调用Comparator接口

  • 这里我展示一下max的比较器
  • 这里可以使用lambda简化,因为Comparator是函数式接口
System.out.println(Collections.max(list,new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2-o1;
    }
}));
  1. swap接口
    这里交换List集合里面的元素,第一个参数是List集合,第二,三个参数都是下标
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);

Collections.swap(list, 0, 9);
System.out.println(list);

结束

那就如此结束吧,捋一遍确实我自己也清晰了,希望大家可以分清楚什么集合用什么方法,感谢观看

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐