JAVA集合基础03---List集合,数据结构,Set集合哈希值,排序器
JAVA集合基础03
List集合概述和特点
-
需要导包,继承Collection
-
有序集合(也成为序列),用户可以精确控制列表中每个元素的插入位置,用户可以通过整数索引访问元素,并搜索列表中的元素
-
与set集合不同,列表通常允许重复的元素
-
有序:储存和取出的元素顺序一致
-
可重复:存储的元素可重复
-
和Collection集合的区别是List集合有索引
public static void main(String[] args) {
//创建集合对象
List<String> list = new ArrayList<String>();
//创建对象
list.add("AAAAA");
list.add("BBBBB");
list.add("CCCCC");
list.add("CCCCC");
//输出集合对象
System.out.println(list);
//迭代器的方式遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
String s = it.next();
System.out.println(s);
}
}
List集合的特有方法
- 父类Collection没有,子类ArrayList拥有
遍历List集合有两种方式
- 遍历器方式
- 传统的增强For循环
并发修改异常
-
ConcurrentModificationException:当不允许这样的修改时,可以通过检测对象的并发修改的方法来抛出此异常
-
假设前面调用了3次add,所以实际修改集合次数modcount=3,然后开始迭代器iterator,预期修改集合次数expectcount就被赋值为3,迭代器运行过程中,采用Next()方法遍历同时(判断集合里面有没有"world"这个元素,如果有,就添加一个"javaee"元素)的条件实现,if又使用了add的方法,从而导致modcount++,但expect不被重新赋值保持3,Next()方法自身会运行两种修改方法的值是否相等的判断,最终导致modcount!=expectcount抛出了并发修改异常
-
使用For循环,get()方法的遍历则不会对两种修改次数种类的值进行是否相等进行判断所以并不会抛出并发异常可以正常运行
List迭代器
ListIterator:列表迭代器
-
通过List集合的ListIterator()方法得到,所以说它是List集合特有的迭代器
-
通过允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器打的当前位置
-
E next() :返回迭代中下一个元素
-
boolean hasNext(): 如果迭代具有更多元素,则返回true
-
E previous():返回列表中的上一个元素
-
boolean hasPrevious():如果在相反方向遍历列表时有更多元素,则返回true
-
void add(E e):将指定的元素插入列表
-
列表List迭代器不会出现并发修改异常
增强For循环
-
简化数组和Collection集合的遍历
-
实现Iterable接口的类允许其对象成为增强型for语句的目标
-
格式
-
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
-
-
内部原理是一个Iterator迭代器
数据结构
栈和队列
-
数据结构是计算机存储,组织数据的方式,是指在相互之间存在一种或多种特定关系的数据元素的集合
-
精心选择的数据结构可以带来更高的运行或者储存效率
-
数据进入栈模型的过程称为:压/进栈
-
数据离开栈模型的过程称为:弹/出栈
-
栈是先进后出的一种数据模型
队列
- 数据从后端进入队列模型的过程称为:入队列
- 数据从前端进入队列模型的过程称为:出队列
- 队列是先进先出的一种数据模型
常见数据结构之数组
- 查询数据通过索引定位,查询任意数据耗时相同,查询效率高
- 删除数据时,要将原始数据删除,同时后面的每个数据前移,删除效率低
- 添加数据时,添加位置后的每个数据后移,再添加元素,添加效率极低
- 数组是一种查询快,增删慢的模型
常见数据结构之链表
- 节点的储存位置(地址),存储具体的数据
- 节点当中还存在下一个节点的位置
- “^”表示结点指向空地址
- 尾插法,尾部的地址替换
- 链表是一种增删快,查询慢的模型(对比数组
- 查询某一个数据是否存在,必须要从头开始
List集合子类的特点
List集合常用子类:ArrayList,LinkedList
- ArrayList:底层数据结构是数组,查询快,增删满
- LinkedList:底层数据结构是链表,查询慢,增删快
Set集合概述和特点
- 不包含重复元素的集合
- 没有带索引的方法,所以不能使用普通for循环遍历
- 实现类HashSet:对集合的顺序不作任何保证
哈希值
-
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
-
Object类中有一个方法可以获取对象的哈希值
-
public int hashCode()
-
返回对象的哈希码值
-
同一个对象多次调用hashCode()方法的哈希值是相同的
-
默认情况下不同对象的哈希值是不一样的,通过方法重写可以实现不同对象的哈希值是相同的
-
HashSet集合概述和特点
- 底层数据结构是哈希表
- 对集合的迭代顺序不做任何保证,也就是说不保证存储和取出的元素顺序一致
- 没有带索引的方法,所以不能使用普通for循环遍历
- 由于是set集合,所以不包含重复元素的集合
HashSet集合保证元素唯一性源码分析
-
要保证元素唯一性,需要重写hasCode()和equals()
-
哈希值是为了解决遍历时间消耗大而设计出来的
-
hashcode值就相当于门牌号,然后房间里分配的空间就是来存储元素的
常见数据结构之哈希表
哈希表
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- JDK8后,在长度比较长的时候,底层实现了优化
LinkedHashSet集合概述和特点
- LinkedHashSet集合特点
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就是说元素的储存和取出顺序是一致的
- 有哈希表保证元素唯一,也就是说没有重复的元素
TreeSet集合概述和特点
- 间接实现Set接口,实现Set接口的子类
TreeSet集合特点
-
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据其元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
-
由于没有带索引的方法,所以不能用普通For循环遍历
-
由于是Set集合,所以不包含重复元素的集合
-
集合只能使用引用类型,或者基本类型的包装类型
自然排序Comparable的使用
-
使用TreeSet无参构造后,对对象类(如Student)进行增加接口Comparable
-
增加接口后,对接口的compareTo方法进行重写
-
return 0;则输出第一个,return 1;则是按照对象输入顺序从先到后遍历,-1则是反过来
-
想要按照输入Student的年龄从小到大
-
int num = this.age - s.age
-
this是谁调用它就是谁,this理解为接下来要存储的元素,s理解为集合里已经存储的元素(比如存储s2时s2就是现在要存储的元素,s1相对就是已经有的元素),相减为正数,则从小到大依次排序,想要升序,this在前面,想要降序this放在后面
-
需要相同年龄不同对象也要遍历的话
-
int num2 = num == 0?this.name.compareTo(s.name):num return num2
-
1.首先考虑字符首字母,按照A—Z排序,如果首字母相同再看下一位字母,以此类推
-
2.若字母相同,例如Student和Students比较,已有的字母相同,那么会继续比较字符串的长度,短的排前面
-
3.这里CamparaTo方法返回值在1和2中分别是返回两个字母的Unicode差值和返回字符串长度的差值,即length的差,都是int类型的返回值
-
结论
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
比较器排序Comparator的使用
public static void main(String[] args) {
TreeSet<Students> tr = new TreeSet<>(new Comparator<Students>() {//匿名内部类
@Override
public int compare(Students s1, Students s2) {
//由于是在Demo类中重写,按照之前的重写方法,this所指的对象则是Demo
//因此传入两个参数,不使用this即可
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Students s1 = new Students("xuzhiyuan", 25);
Students s2 = new Students("zhoujielun", 29);
Students s3 = new Students("pengyuyan", 21);
Students s4 = new Students("honghuihuang", 24);
Students s5 = new Students("xuxian", 24);
tr.add(s1);
tr.add(s2);
tr.add(s3);
tr.add(s4);
tr.add(s5);
for (Students s : tr) {
System.out.println(s.getName() + "," + s.getAge());
}
}
- 使用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序
- Comparator默认从小到大排列,前一个数减去后一个数,大于0调换位置,否则不变
- 比较器排序就是让集合构造方法接收Comparator的实现类对象,重写compare(To1,To2)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
public static void main(String[] args) {
//获取10个1-20的随机数,要求不能重复,并且输出
Set
//Set
Random r = new Random();
while (s.size()<10){
int num = r.nextInt(20)+1;
s.add(num);
}
System.out.println(s);//直接输出也可
//or
for (Integer i : s) {
System.out.println(i);
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了