当前位置: 首页 > news >正文

java集合之单列集合

目录

一、集合概述

1、集合

①基础概念

②与数组的对比

③集合分类:

2、集合框架

①基础概念

②三要素

③集合框架继承体系图(重要部分,并非全部)

二、单列集合

1、概述

2、Collection接口

①Collection基础方法

②Collection泛型参数方法补充

3、List接口

①特点:        

②常用方法:

③实现类:

1>  ArrayList

2>  LinkedList

3>  Vector

4、Set接口

①特点:

②常用方法:

③实现类:

1>  HashSet

2>  TreeSet

排序规则

3>  LinkedHashSet


大学生入门:引用数据类型——数组及其易踩坑的点-CSDN博客https://blog.csdn.net/qq_73698057/article/details/149564809?spm=1001.2014.3001.5502

数组特点:

长度固定:

        创建时需要指定长度,并且长度在创建后不可改变

相同数据类型:

        例如int数组只能存储int类型的数据

连续内存分配:

        堆空间为数组开辟的内存是连续的

        导致在插入和删除元素时需要元素整体移动,效率低下

随机访问:

        由于数组中的元素在内存中是连续存储的

        可以通过索引来访问,时间复杂度为O(1)

显而易见

如果我们要存储多个数据类型不一致

或者个数不固定时

数组就无法满足我们的需求了

这时就需要使用 java 提供的集合框架

一、集合概述

1、集合

①基础概念

java中,集合是一种用于存储和操作一组对象的数据结构

它提供了一组接口和类,用于处理和操作对象的集合

②与数组的对比

为了更好地理解集合,我们把集合和数组放在一起来看:

首先他们都可以存储多个元素值

区别在于:

1. 数组的长度是固定的

    集合的长度是可变的

2. 数组中存储的是同一类型的元素

    集合中存储的数组可以是不同类型的

3. 数组可以存放基本类型数据或引用类型变量

    集合中只能存放引用类型变量

4. 数组除了拥有 length属性 和 从Object中继承过来的方法之外,数组对象调用不了其他的属性和方法了

    集合框架由 java.util 包下多个接口和实现类组成,定义并实现了很多方法,功能强大

       

只能存引用类型变量?

那下面这些情况是怎么回事?

collection.add(1);

int a = 1;

collection.add(a);

Integer integer = 1;

collection.add(integer);

        

实际情况是

前两种自动装箱

最后一种直接存储 Integer 对象

所以“集合只能存储引用类型”这个说法是正确的

集合分类:

1.单列集合(Single Column Collection)
        根接口: java.util.Collection
        单列集合是指每个集合元素只包含一个单独的对象,它是集合框架中最简单的形式

Collection接口结构图(重要部分,并非全部)


1.多列集合(Multiple Column Collection)
        根接口: java.util.Map
        多列集合是指每个集合元素由多个列(字段)组成,可以同时存储和操作多个相关的值

Map接口结构图如下(重要部分,并非全部)

2、集合框架

①基础概念

集合框架

        java中用于表示和操作集合的一组类和接口

        位于 java.util 包中

        包括:

                Collection 集合接口

                List            列表接口

                Map           映射接口

                Set             类

                ... ...

        

        目的:

                提供一种通用的方式来存储和操作对象的集合

                无论集合的具体实现方式如何

                用户都可以使用统一的接口和方法来操作集合

怎么理解 集合 集合框架 呢 ?

如果把一个个集合看作一个个收纳盒

那么集合框架就是 收纳指南

上面记录了:

不同类型的收纳盒、

统一的操作标准、

还有现成的使用方法

②三要素

1.接口

        整个集合框架的上层结构,都是用接口进行组织的

        接口中定义了集合中必须要有的基本方法

        通过接口还把集合划分成了几种不同的类型,每一种集合都有自己对应的接口

        

2、实现类

        对于上层使用接口划分好的集合种类,每种集合接口都会有对应的实现类

        每一种接口的实现类很可能有多个,每个的实现方式也各有不同

        

3、数据结构

        每个实现类都实现了接口中所定义的最基本的方法

        例如对数据的存储、检索、操作等方法

        但是不同的实现类,它们存储数据的方式不同,也就是使用的数据结构不同

③集合框架继承体系图(重要部分,并非全部)

二、单列集合

1、概述

        单列集合是指每个集合元素只包含一个单独的对象

        单列集合的根接口是 java.util.Collection 

        根接口下面又有两个子接口:ListSet

如何理解这句话呢?

我们把单列和多列放一起比较

就好比有一个班级花名册

单列集合每一行只有单独的名字信息,而多列集合一行有多个信息:

单列                                        双列

        

再比如说有一列火车

单列火车是这样的:

[苹果车厢] [牛奶车厢] [面包车厢] [鸡蛋车厢]
// 每节车厢只装一种商品

双列火车是这样的:

[苹果-5元车厢] [牛奶-10元车厢] [面包-8元车厢]
// 每节车厢装两种信息:商品名和价格

2、Collection接口

单列集合的父接口

其中定义了单列集合通用的一些方法

Collection接口的实现类都可以使用这些方法

①Collection基础方法

package java.util;
public interface Collection<E> extends Iterable<E> {//省略...//向集合中添加元素 boolean add(E e)//清空集合中所有的元素。    void clear()//判断当前集合中是否包含给定的对象。boolean contains(Object o)//判断当前集合是否为空。boolean isEmpty()//把给定的对象,在当前集合中删除。boolean remove(Object o)//返回集合中元素的个数。  int size()//把集合中的元素,存储到数组中。    Object[] toArray()
}

示例:

import java.util.ArrayList;
import java.util.Collection;public class Test {// 集合中 一般 也放 同一种类型数据public static void main(String[] args) {// 1.接口 引用 = new 实现类(实参);Collection coll = new ArrayList();if (coll.isEmpty())System.out.println("coll is Empty!");// 2.任何引用类型都可以放入// 自动扩容coll.add("hello"); //StringInteger i = 12;coll.add(i);coll.add(2.3); // Doublecoll.add(1.2F); // Floatcoll.add('a'); // Characterint[] arr = { 1, 2, 3 };coll.add(arr); //int[]// coll.add(new Student());// 3.输出 coll.toString()System.out.println(coll);System.out.println("size: " + coll.size());System.out.println("--------------");// 4.清空 collcoll.clear();System.out.println(coll);// 5.判断是否为空if (coll.isEmpty())System.out.println("coll is Empty!");elseSystem.out.println("coll is not empty!");}
}

此时动手实操的同学会发现代码爆黄线了

为什么呢?

其实是我们的格式有点小问题

区别于之前的书写格式,集合是这样的:

接口类型<存储的数据类型> 接口引用名 = new 实现类<>(构造方法实参);

例: Collection<String> coll = new ArrayList<>();

        

还可以写成:

Collection<String> coll = new ArrayList<String>();

Collection coll = new ArrayList();     

        

需要注意的是,前两种明确了数据类型,就只能存储这一种

第三种可以存储多种,但是一般情况下存储一种数据类型

②Collection泛型参数方法补充

package java.util;
public interface Collection<E> extends Iterable<E> {//省略... //把一个指定集合中的所有数据,添加到当前集合中    boolean addAll(Collection<? extends E> c)//判断当前集合中是否包含给定的集合的所有元素。    boolean containsAll(Collection<?> c)//把给定的集合中的所有元素,在当前集合中删除。    boolean removeAll(Collection<?> c)//判断俩个集合中是否有相同的元素,如果有当前集合只保留相同元素,如果没有当前集合元素清空    boolean retainAll(Collection<?> c)//把集合中的元素,存储到数组中,并指定数组的类型  <T> T[] toArray(T[] a)//返回遍历这个集合的迭代器对象    Iterator<E> iterator()
}

示例:

import java.util.ArrayList;
import java.util.Collection;
//泛型方法测试
public class Test03_Element {public static void main(String[] args) {//1.实例化两个集合对象,专门存放String类型元素// 集合实例化对象 固定写法Collection<String> c1 = new ArrayList<>();Collection<String> c2 = new ArrayList<>();//2.分别往c1和c2集合中添加元素String s1 = "hello";String s2 = "world";c1.add(s1);c1.add(s2);String s3 = "nihao";String s4 = "hello";String s5 = "okok";c2.add(s3);c2.add(s4);c2.add(s5);System.out.println("c1: " + c1);System.out.println("c2: " + c2);System.out.println("-------------");//3.将c2集合整体添加到c1中c1.addAll(c2);System.out.println("c1.size: " + c1.size());System.out.println("after addAll(c2), c1: " + c1);System.out.println("-------------");//4.判断是否包含指定元素boolean f = c1.contains("hello");System.out.println("contains hello: " + f);//5.创建s6对象,判断集合中是否包含该对象// 注意: s6的地址 和 "world"地址不一样// s6是堆中临时new出来的,"world"存在堆中的字符串常量池中String s6 = new String("world");// 结果显示true,说明集合contains方法借助equals方法进行比较,而非 ==f = c1.contains(s6);System.out.println("contains(s6): " + f);System.out.println("-------------");//6.判断是否包含c2对象f = c1.containsAll(c2);System.out.println("containsAll(c2): " + f);System.out.println("-------------");//7.删除指定元素【底层借助equals比较,然后删除】f = c1.remove(s6);System.out.println("remove(s6): " + f);System.out.println("after remove, c1: " + c1);System.out.println("-------------"); //8.删除c2整个集合【底层实现:遍历c2,逐个元素equals比较,然后删除】f = c1.removeAll(c2);System.out.println("removeAll(c2): " + f);System.out.println("after remove, c1: " + c1);}
}

3、List接口

继承了 Collection 接口

①特点:        

        ——有序集合

        ——带索引的集合,可以通过索引精确查找对应元素

        ——可以存放重复元素(包括null)

        ——默认尾部添加元素

②常用方法:

除了从 Collection 中继承过来的方法,除此之外还有:

//返回集合中指定位置的元素。
E get(int index);//用指定元素替换集合中指定位置的元素,并返回被替代的旧元素。
E set(int index, E element);//将指定的元素,添加到该集合中的指定位置上。
void add(int index, E element);//从指定位置开始,把另一个集合的所有元素添加进来
boolean addAll(int index, Collection<? extends E> c);//移除列表中指定位置的元素, 并返回被移除的元素。
E remove(int index);//查收指定元素在集合中的所有,从前往后查到的第一个元素(List集合可以重
复存放数据)
int indexOf(Object o);//查收指定元素在集合中的所有,从后往前查到的第一个元素(List集合可以重
复存放数据)
int lastIndexOf(Object o);//根据指定开始和结束位置,截取出集合中的一部分数据
List<E> subList(int fromIndex, int toIndex);

示例:

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class Test {public static void main(String[] args) {//1.创建List集合对象List<String> list = new ArrayList<>();//2.添加元素,默认尾部添加list.add("hello1");list.add("hello2");list.add("hello3");list.add("hello1");System.out.println(list);//3.指定位置添加元素// add(int index,String s) list.add(1, "world");System.out.println(list);//4.删除索引位置为2的元素boolean f = list.remove(2);System.out.println("remove(2): " + f);System.out.println("after remove: " + list);//5.修改指定位置元素list.set(0, "briup");System.out.println(list);//6.借助get方法遍历集合for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));    }System.out.println("-----------------");//7.使用foreach遍历for(Object obj : list){System.out.println(obj);}System.out.println("-----------------");//8.使用迭代器遍历Iterator<String> it = list.iterator();while(it.hasNext()){String str = it.next();System.out.println(str);}}
}

③实现类:

1>  ArrayList

是 List 的一个实现类

是最常用的一种 List 类型集合

extends AbstractList<E> extends AbstractCollection<E>

特点:

        1. ArrayList集合底层是数组,查询快,增删慢

            适用业务场景:查询功能多,增删功能少

            eg:学生管理系统

        2. ArrayList集合线程不安全

代码示例与 LinkedList 放在一起对比

2>  LinkedList

实现了List接口

extends AbstractSequentialList<E> extends AbstractList<E>

特点:

        1. LinkedList 集合底层是双向链表,查询慢,增删快

            适用业务场景:增删功能多,查询功能少

            eg:浏览器的前进后退

        2. LinkedList 集合线程也不安全

特点验证:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;public class Test {public static void main(String[] args) {//操作集合的次数final int NUM = 100000;//1.实例化集合对象List<String> list = new ArrayList<>();//List<String> list = new LinkedList<>();//2.开启计时,往集合种放入 100000 个元素long start1 = System.currentTimeMillis();for (int i = 0; i < NUM; i++) {list.add(0,"hello"+i);}long end1 = System.currentTimeMillis();//3.输出时长System.out.println(list.getClass().getSimpleName()+"插入"+NUM+"条数据耗时"+(end1-start1)+"毫秒");//4.开启计时,从集合种取 100000 个元素long start2 = System.currentTimeMillis();for(int i = 0; i < list.size(); i++){list.get(i);}long end2 = System.currentTimeMillis();//5.输出时长System.out.println(list.getClass().getSimpleName()+"检索"+NUM+"条数据耗时"+(end2-start2)+"毫秒");}
}//运行效果:
//根据电脑的当前情况,每次运行的结果可能会有差异
//以下是我的电脑运行的实验结果ArrayList插入100000条数据耗时508毫秒
ArrayList检索100000条数据耗时2毫秒
LinkedList插入100000条数据耗时22毫秒
LinkedList检索100000条数据耗时17709毫秒

System.currentTimeMillis();

//获取当前时刻的时间戳

3>  Vector

实现了 List 接口

extends AbstractList<E> extends AbstractCollection<E>

特点:

        1. Vector 集合底层是数组,查询快,增删慢

        2. Vector 集合多线程安全,效率低

4、Set接口

继承了 Collection 接口

①特点:

        ——无序集合,不按 add 顺序添加元素

        ——不带下标(索引)

        ——元素不能重复

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;public class Test {public static void main(String[] args){//1.实例化Set集合,指向HashSet实现类对象Set<String> set = new HashSet<>();set.add("hello1");set.add("hello2");set.add("hello3");set.add("hello4");set.add("hello5"); //添加失败 重复元素set.add("hello5"); //添加失败 重复元素//加强for循环遍历for(String obj : set){System.out.println(obj);}System.out.println("-----------------");//迭代器遍历Iterator<String> it = set.iterator();while(it.hasNext()){Object obj = it.next();System.out.println(obj);}}
}//输出结果:
hello1
hello4
hello5
hello2
hello3
----------
省略...

②常用方法:

基本都是Collection继承过来的,这里就不再赘述

③实现类:

1>  HashSet

底层是哈希表

extends AbstractSet<E> implements Set<E>

特点:

        1. 无序性:存储顺序和插入顺序无关

        2. 唯一性:元素不重复,null也只能存一个

        3. 高效性:哈希表的特点

需要注意的是,由于HashSet没有重写 equals方法 和 hashCode方法

所以存储 数据相同的自定义类的对象 时会认为它们是不同的

比如:

import java.util.*;class Student {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}// 注意:这里没有重写 equals 和 hashCode 方法@Overridepublic String toString() {return "Student{name='" + name + "', age=" + age + "}";}
}public class HashSetDuplicateExample {public static void main(String[] args) {Set<Student> set = new HashSet<>();// 添加四个"相同"的Student对象set.add(new Student("Alice", 20));set.add(new Student("Alice", 20));set.add(new Student("Alice", 20));set.add(new Student("Alice", 20));System.out.println("HashSet大小: " + set.size()); // 输出 4System.out.println("HashSet内容: " + set);}
}

所以要往 HashSet 集合存储自定义类

一定要记得重写自定义类中的equals方法和hashCode方法

2>  TreeSet

底层是二叉树

TreeSet是SortedSet(Set接口的子接口)的实现类

特点:

        1. 有序性:插入的元素会自动排序

        2. 唯一性:元素不重复,null也只有一个

        3. 高效性:二叉树的特点

入门案例一:

import java.util.Set;
import java.util.TreeSet;public class Test{public static void main(String[] args){//1.实例化集合对象Set<Integer> set = new TreeSet<>();//2.添加元素set.add(3);set.add(5);set.add(1);set.add(7);//3.遍历for(Integer obj : set) {System.out.println(obj);}    }
}//输出结果:
1
3
5
7

截止这里一切都没有问题,我们继续往下看

入门案例二:

import java.util.Set;
import java.util.TreeSet;class Person {private String name;private int age;public Person() {}public Person(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;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}
}public class Test_Person {public static void main(String[] args) {//1.实例化TreeSetSet<Person> set = new TreeSet<>();//2.添加元素set.add(new Person("zs",21));set.add(new Person("ls",20));set.add(new Person("tom",19));//3.遍历集合for (Person person : set) {System.out.println(person);}}
}//程序运行,提示异常,具体如下:
Exception in thread "main" java.lang.ClassCastException: 
包名.Person cannot be cast to java.lang.Comparableat java.util.TreeMap.compare(TreeMap.java:1290)at java.util.TreeMap.put(TreeMap.java:538)at java.util.TreeSet.add(TreeSet.java:255)at 
包名.Test_Person.main(Test_Person.java:41)

看起来似乎没有问题啊

为什么会报错呢?

我们看一下报错原因:

ClassCastException:Person cannot be cast to java.lang.Comparable

原来是我们自定义的类没有实现 Comparable 接口

为什么会这样呢?

这就不得不引入新的知识点了:

排序规则

由于TreeSet是一个有序集合,那么想要有序必然要存在一定的规则

有两种方式

1、自然排序

        如果一个类实现了Comparable接口

        并且重写了compareTo方法,那么这个类对象就可以进行排序了。

        

        compareTo方法说明:

                返回值:int

                        小于0:当前对象小,this.value < o.value

                        等于0:当前对象等于 this.value = o.value

                        大于0:当前对象大  this.value > o.value

                调用者:当前对象的参数

                实际参数:要比较的对象参数

                调用方式:this.value.compareTo(o.value);

                作用:

                        元素插入过程:先排序后去重

                        当TreeSet插入元素时会调用compareTo方法

                        如果方法返回-1,先插入this,o放后面
                        如果方法返回0,两个元素相等,只插入一个元素
                        如果方法返回1,先插入o,this放后面

2、比较器排序

        如果Student不是自定义类,而是第三方提供的类

        我们不可以修改源码,就可以使用比较器排序

        

        实现:

                1、创建一个实现了 Comparator 接口的类
                2、重写里面的 compare() 方法
                3、创建一个 TreeSet 类对象时,将比较器对象作为参数传递给构造函数

        compare方法说明:

                int result = compare(o1,o2);

                ——result的值大于0,表示升序

                ——result的值小于0,表示降序

                ——result的值等于0,表示元素相等,不能插入

       

注意:

        如果自然排序和比较器排序同时存在

        优先使用比较器排序

案例展示:

public class Teacher implements Comparable<Teacher>{private int id;private String name;private int age;public Teacher() {}public Teacher(int id, String name, int age) {this.id = id;this.name = name;this.age = age;}public int getId() {return id;}public String getName() {return name;}public int getAge() {return age;}@Overridepublic String toString() {return "Teacher [id=" + id + ", name=" + name + ", age=" + age + "]";}@Overridepublic int compareTo(Teacher o) {int num = this.name.compareTo(o.name);if(num == 0){num = this.age - o.age;}if(num == 0){num = this.id - o.id;}return num;}
}import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;public class TestTeacher {public static void main(String[] args) {//自然排序,Comparable接口Teacher t1 = new Teacher(001,"alan", 18);Teacher t2 = new Teacher(002,"tom", 19);Teacher t3 = new Teacher(003,"tom", 20);Teacher t4 = new Teacher(004,"alan", 18);Teacher t5 = new Teacher(005,"jack", 18);Set<Teacher> set = new TreeSet<>();System.out.println("自然排序:");set.add(t1);set.add(t2);set.add(t3);set.add(t4);set.add(t5);for(Teacher t : set){System.out.println(t);}//比较器排序,Comparator接口Comparator<Teacher> comparator = new Comparator<Teacher>() {@Overridepublic int compare(Teacher t1, Teacher t2){ int num = t1.getName().compareTo(t2.getName());if(num == 0){ num = t1.getAge() - t2.getAge();}if(num == 0){num = t1.getId() - t2.getId();}return num;}};Set<Teacher> set2 = new TreeSet<>(comparator);System.out.println("比较器排序:");set2.add(t1);set2.add(t2);set2.add(t3);set2.add(t4);set2.add(t5);for(Teacher t : set2){System.out.println(t);}}
}
3>  LinkedHashSet

底层是链表 + 哈希表

父类 HashSet 所以具有父类唯一、高效的优点
接口 Set     保持元素的插入顺序,不会自动排序

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;public class Test074_LinkedHashSet {public static void main(String[] args) {// 1.实例化LinkedHashSetSet<String> set = new LinkedHashSet<>();// 2.添加元素set.add("bbb");set.add("aaa");set.add("abc");set.add("bbc");set.add("abc");// 3.迭代器遍历Iterator<String> it = set.iterator();while (it.hasNext()) {System.out.println(it.next());}}
}//运行结果:
bbb
aaa
abc
bbc

http://www.lryc.cn/news/620262.html

相关文章:

  • 基于离散余弦变换的激活水印(DCT-AW)
  • TCP Socket 编程实战:实现简易英译汉服务
  • Devextreme-vue + Vue2日历下拉框的使用
  • MySQL优化常用的几个方法
  • 《量子雷达》第3章 量子雷达的传输与散射 预习2025.8.13
  • 上下文工程
  • Spring Boot 整合 Thymeleaf 模板引擎:从零开始的完整指南
  • Qwen大模型加载与文本生成关键参数详解
  • lesson37:MySQL核心技术详解:约束、外键、权限管理与三大范式实践指南
  • 第一章 OkHttp 是怎么发出一个请求的?——整体流程概览
  • 浏览器面试题及详细答案 88道(23-33)
  • 智能制造数字孪生最佳交付实践:打造数据融合×场景适配×持续迭代的数字孪生框架
  • 【LeetCode】6. Z 字形变换
  • 公用表表达式和表变量的用法区别?
  • Linux 5.15.189-rt87 实时内核安装 NVIDIA 显卡驱动
  • LeetCode215~ 234题解
  • ACWing 算法基础课-数据结构笔记
  • Leetcode题解:215,数组中的第k个最大元素,如何使用快速算法解决!
  • 把 Linux 装进“小盒子”——边缘计算场景下的 Linux 裁剪、启动与远程运维全景指南
  • C#+Redis,如何有效防止缓存雪崩、穿透和击穿问题
  • 联网车辆功能安全和网络安全的挑战与当前解决方案
  • OpenBMC中的BMCWeb:架构、原理与应用全解析
  • 直播美颜SDK开发实战:高性能人脸美型的架构与实现
  • C++调试革命:时间旅行调试实战指南
  • 图像优化:使用 Next.js 的 Image 组件
  • h5bench(4)
  • linux 内核 - 内存管理概念
  • Linux 服务部署:自签 CA 证书构建 HTTPS 及动态 Web 集成
  • GO学习记录四——读取excel完成数据库建表
  • [AXI5]AXI协议中awsize和awlen在Vector Atomic地址膨胀中的作用