JDK源码学习

article/2025/9/19 9:32:14

Java集合框架之Collection集合

http://www.leocook.org/category/#java

Java的集合框架分两大系Collection系和Map系,之所以要分开是因为Collection内存储的是线性集合,而 Map的元素是以键值对(Key-Value)的形式存的。其实Map和Collection内部实现是存在联系的,学完就懂了。本文主要介绍的 Collection中常用的集合的用法以及之间的区别。

一、引言
Java集合框架和IO框架一样, 看似很庞杂的 体系框架,但是当你逐一 深入每个集合的用法后,就能明显的看出他们之间的区别和联系。最后拎出一个框架图,就一目了然了。由于Java的集合框架分两大系Collection系 和Map系,之所以要分开是因为Collection内存储的是线性集合,而Map的元素是以键值对(Key-Value)的形式存的。其实Map和 Collection内部实现是存在联系的,学完就懂了。本文主要介绍的Collection中常用的集合的用法以及之间的区别。

二、Collection集合框架介绍
下 面这张图,是Collection框架的类图,中间省略的几个无关紧要的类,整体还算详细。斜体加粗表示接口(规范的类图应该是加上

这里写图片描述

这里写图片描述

Java集合框架之Collection集合

Collection接口是根接口,Iterable接口可以简单理解成标记接口,它约定了所有线性集合(数组、队列、栈都属于线性集合,Map是二维集合不能直接遍历)都必须可以遍历,同时也定义了hasNext()、next()、和remove()三个遍历方法, 后 边还会详细讲到。从根接口派生出两个子接口Set和List,分别作为Set集合和List集合的根接口。最后是五个常用的集合类HashSet、 TreeSet、LinkedList、ArrayList、Vector。不过从类图中发现每个集合接口都有一个抽象的实现类,而且每个最终集合类同时 继承了抽象类和实现了集合接口。 例如:ArrayList extends AbstractList implements List, AbstractList本身就已经实现了List接口,这里再写implements List是为了使整个集合框架结构更清晰,很多集合框架图为了看着方便都省略了实现接口这条虚线,但是我觉得需要解释。

Set和List具体有什么区别呢,我们先来看下面这张Collection、List和Set的类图。List接口的类图中只列出特有的方法,从 Collection中继承的方法没有重复列举。为什么没有Set接口?Set接口中的方法和Collection完全一样,没有增加任何新的方法,就不 再重复画图了。Set再重复定义一遍Collection接口中的方法,一是为了作为类库要添加属于Set的特有方法约定,虽然方法定义相同,但是具体方 法实现是不同的,例如Set的add方法不能添加重复元素,List则可以;二是为了让集合框架结构更清晰。

Java集合框架之Collection集合

主要区别:

1、List必须是有序集合,Set可以有序也可以无序,这里的有序无序是指集合内部的存储顺序是否和元素的添加顺序相同。其实Set接口注释中并没有特 别说明Set就应该是无序的,但也没有像List接口一样开始就声明“An ordered collection ”,但是在Set集合的iterator()方法注释中有这样一句话:The elements are returned in no particular order (unless this set is an instance of some class that provides a guarantee).结论就是Set的实现类也可以有序。

2、List允许有重复元素,更确切地讲,List允许满足e1.equals(e2)的元素对e1、e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。而Set集合不允许有重复元素,如果允许有null值,最多只能有一个null值。

3、List能对元素精准定位,可以根据元素下标对任意位置的元素实现增删改查。索引下标和数组一样从0开始。例如List接口中定义了 add(int index, E element)、get(int index)、set(int index, E element)等可以访问指定位置元素的方法。Set只能对集合遍历而不能进行随机访问元素。

把List都放在前面说,好像List要比Set优秀似得,并非如此,各有优势,下面详细对5个集合类详细比较 。

三、Collection集合类的实现原理和具体区别
1、ArrayList

ArrayList是比较常用的一个集合,之所以叫数组列表是因为其底层是用数组实现的,数组我们熟悉啊,那就先来看一下数组有什么特点吧。如果是对象数 组,元素可以为null,元素可以重复N次,可以用元素下标访问任意位置(不要越界),而这些现在都成了ArrayList的特点。数组还有一个大特点就 是查询速度很快,这里说一下数组的寻址方式。当创建一个数组时,会在内存中分配一段地址连续的空间,数组名就是这块内存空间的首地址。比如int[] arr=new int[10]; 假设首地址是22005,int[3]就是访问第4个元素,其地址是22005+4*sizeof(int),int大小为4,那么结果就是 22005+4*4=22021,直接去222021这个位置区拿数据就行了。很明显,数组是直接寻址,查找速度相当快。但是如果根据内容查找,就只能遍 历数组了,ArrayList有些个方法indexOf(Object o)、lastIndexOf(Object o)、remove(Object o)都是遍历数组查找元素的,如果数据量很大的话,经测试,性能有所下降。简单点理解就是,ArrayList就是数组的一个封装类,能用数组的地方都能 用它。最后说一下,ArrayList内部数组的增长策略是oldLength>>1,每次增长原来的一半的说法并不准确。

2、LinkedList
LinkedList内部是一个双向链表,学过数据结构的都知道双向链表,删除添加很方便,直接修改前后两个元素的指针就搞定了。 所以LinkedList中提供了很多方便双向增删元素的方法,下面看一下其类图,只列举了LinkedList中特有的方法。

Java集合框架之Collection集合

可以看到有很多类似xxxFrist()和xxxLash()的方法,这真是充分发挥了双向链表的作用。有了这些方法就可以将链接列表用作堆栈、 队列或双端队列。例如操作堆栈用pop()弹出最上面的元素,push()压入栈。其实这些方法只是用户接口,仅仅是换了换方法名字,其中道理看下源码便 知。

java”>
public E peek() {
final Node f = first;
return (f == null) ? null : f.item;
}
public E poll() {
final Node f = first;
return (f == null) ? null : unlinkFirst(f);
}
public boolean offer(E e) {
return add(e);
}
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public E peekFirst() {
final Node f = first;
return (f == null) ? null : f.item;
}
public E peekLast() {
final Node l = last;
return (l == null) ? null : l.item;
}
public E pollFirst() {
final Node f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node l = last;
return (l == null) ? null : unlinkLast(l);
}
可以看到方法调来调去最后就是在访问链表头尾的元素。这也给我们一个提示,好的代码方法命名真的很重要!这些方法都是在LinkedList中单独定义 的,所以想用这些方法,就不能用多态的方式去创建对象,如List list=new LinkedList();这样只能调用List中定义的方法。链表还有一个很坑的特点就是,如果要找一个元素每次都要遍历一下链表,查找速度略慢。另外 Linkedlist使用了一个特殊的迭代器listIterator,也是其特有的,这个迭代器的特殊之处就是可以从双向迭代,即可从前往后迭代也可从 后往前迭代,其实现还是要依赖于双向链表,使用比较方便,应该记住。

3、Vector

Vector是从JDK1.0就有了,它是集合框架在JDK1.2出现后才加入到集合框架的,所以这个Vector很古老了,其功能和 ArrayList基本一样,内部也是数组实现。唯一不同就是Vector是线程同步,Arraylist线程不同步。这里不再赘述了,此类已基本弃用!

4、HashSet

Set集合的特点就是无重复元素,那么HashSet是怎么实现元素的唯一性呢?通过比较hashcode和equals,如果hashcode相同,才会继续比较equals,如果hashcode不同,不再比较equals,上个例子说明:

package com.heima.collection;
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
A a1=new A(“hector”,22);
A a3=new A(“paul”,33);
A a4=new A(“zeus”,100);
A a5=new A(“zeus”,100);
HashSet set=new HashSet();
set.add(a1);
set.add(a3);
set.add(a4);
set.add(a5);
for(Iterator it=set.iterator();it.hasNext();){
System.out.println(it.next());
}

}
}
class A{
private int age;
private String name;
public A() {
super();
}
@Override
public String toString() {
return “[age=” + age + “, name=” + name + “]”;
}
public A(String name, int age) {
super();
this.name = name;
this.age = age;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
System.out.println(“hashcode…”+this.getName());
return 60;
}
@Override
public boolean equals(Object obj) {
//name和age都相等,为true
A a=(A)obj;
return this.getName().equals(this.getName())&&a.getAge()==this.getAge();
}

}
运行结果:

Java集合框架之Collection集合

这里重写了hashcode()和equals(),故意让hashcode返回固定值60,通过运行结果可以看到当 hashcode相等时会继续判断equals,equals为true就视为重复元素,false则添加,如果不重写hashcode你会发现不会调用 equals方法。因为hasdcode和equals方法都是继承自超类Object,Object中默认hashcode是根据对象的内存地址计算 的,equals用“==”比较,所以不同对象的hashcode一定不同,equals也不会为true。但是java中有约定: 如果两个对象相等(equal),那么必须拥有相同的哈希码(hash code),所以一般都会同时重写equals和hashcode,这个以后慢慢再说。

HashSet还有一个特点就是无序,HashSet内部是一个哈希表(又称散列表),每一个输入都会经过哈希函数得到一个固定长度的哈希值hash(Key)=hashcode,元素的存储位置是根据这个哈希值而定的,所以才会显得无序。

4、TreeSet

前面说过Set集合也可以有序,TreeSet就是那个有序的集合类,从总体框架图中可以看到TreeSet并没有直接实现Set接口而是实现了 其子接口Sorted,该接口进一步提供了关于元素总体排序的Set,这些元素使用其自然顺序进行排序,或者根据通常在创建有序 set 时提供的Comparator进行排序。就是是的TreeSet中的元素必须提供排序规则。对TreeSet元素排序有两种方法,一是元素本身具有排序规 则就是实现Comparable接口,而是给TreeSet提供一个Comparator比较器。

public class CompareDemo {
public static void main(String[] args) {
Student stu1=new Student(“hector”,29);
Student stu2=new Student(“guoke”,23);
Student stu3=new Student(“paul”,40);
Student stu4=new Student(“aple”,23);
Student[] stu={stu1,stu2,stu3,stu4};
TreeSet tree=new TreeSet();
tree.add(stu1);
tree.add(stu2);
tree.add(stu3);
tree.add(stu4);
Iterator it=tree.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
//实现Comparable接口
class Student implements Comparable{
public int age;
public String name;
public Student() {
super();
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
//实现compareTo方法,定义比较规则,先按age排序,age相同按name排序
//该方法是内部自动调用
@Override
public int compareTo(Object o) {
int res=this.age-((Student)o).age;
if(res==0)
return this.name.compareTo(((Student)o).name);
return res;
}
@Override
public String toString() {
return “Student [age=” + age + “, name=” + name + “]”+”\n”;
}
}
这是第一种实现方法,Student 类实现了Comparable接口,这就使得Student自己有了排序规则。第二种就是给TreeSet提供一个比较器:

class StuComparator implements Comparator{
@Override
public int compare(Student o1, Student o2) {
int res=o1.age-o2.age;
if(res==0)
return o1.name.compareTo(o2.name);
return res;
}
}
创建TreeSet时把比较器传过去就行TreeSet tree=new TreeSet(new StuComarator());同样可以实现排序效果,如果两种方式同时存在,以这种方式为主!另外注意一点,如果这两种方式都没有,程序会出现异常 ClassCastException。

第二种方式更灵活,可以让TreeSet的一个实例定义自己的排序规则,不管存储什么对象都使用同一种排序方式,而且很方面程序的扩展,为首选方式。

第二个特点就是没有重复元素了,TreeSet底层是一个红黑树(又称自平衡二叉树),这个数据结构略微复杂,在TreeMap中详细说明。

四、集合特点比较
Java集合框架之Collection集合


http://chatgpt.dhexx.cn/article/4TEVJoEp.shtml

相关文章

如何阅读Java源码?

阅读Java源码的前提条件: 1、技术基础 在阅读源码之前,我们要有一定程度的技术基础的支持。 假如你从来都没有学过Java,也没有其它编程语言的基础,上来就啃《Core Java》,那样是很难有收获的,尤其是《深入J…

JDK源码学习-基础

JDK源码学习 目录 基础 1. 安装 1.1 下载JDK 1.2 配置环境变量 1.3 验证 2. 简单的程序 2.1 编写代码 2.2 编译文件 2.3 执行类 3. java基本类型 基础 1. 安装 1.1 下载JDK Java下载地址:https://www.oracle.com/technetwork/java/javase/downloads/in…

bug修复---同时触发mousedown事件和touchstart事件

这两天遇到一个bug&#xff0c;每次点击下方键盘时&#xff0c;点击一次数字&#xff0c;文本框中会出现两次重复值&#xff0c;如下所示&#xff1a; 而且console中还有以下报错信息&#xff1a; 查看项目代码&#xff0c;代码如下&#xff1a; <ul><liv-for"…

Vue下 touchstart touchend 事件无效失效解决办法

Vue下 touchstart touchend 事件无效失效解决办法 <van-button:disabled"isLoading"plaintype"info"touchstart.native.prevent"touchstart"touchend.native.prevent"touchend"style"width:40%;height: 40px;"round>对…

触屏touchstart 与 click

设计效果&#xff1a;当手指点击或触摸红框线menuList之外的部分时&#xff0c;弹框menuList消失。 问题&#xff1a;在优化触屏版的时候发现如图问题&#xff0c;当menuList弹出&#xff0c;手指触摸屏幕向下滑动时&#xff0c;menuList弹框不消失&#xff0c;只有手指点击men…

移动端事件touchstart touchmove touchend 动画事件 过渡事件

在移动端新增了touch事件&#xff0c;因为手指的行为叫做“触摸”&#xff0c; 鼠标的行为叫做“点击” 但是它仍然支持点击事件&#xff0c;有300ms的延迟&#xff0c;检测是否双击 移动端的三个事件 touchstart&#xff1a;触摸开始 绑定方式&#xff1a; dom.addEve…

微信小程序、uniapp使用touchstart和touchmove左右滑动删除。以及解决上下抖动问题。

展示效果图直接上代码 <viewv-for"(item, index) in arr":key"item.id"touchstart"touchstart($event)"touchmove"touchmove(index, $event)":class"[touch-item, item.isTouchMove ? touch-move-active : ]" ><…

html按键使用touch,HTML touchstart事件用法及代码示例

每当用户触摸HTML元素时&#xff0c;就会使用touchstart事件执行脚本。触摸特定元素时&#xff0c;如果touchstart事件与之关联&#xff0c;则可以使用它触发javascript函数。 注意&#xff1a;touchstart事件仅在触摸屏设备上起作用。 用法: object.ontouchstart myScript; 以…

JavaScript touch 事件 touchstart touchmove touchend

JavaScript touch 事件 touchstart touchmove touchend MDN 官方文档&#xff1a; https://developer.mozilla.org/en-US/docs/Web/API/Touch_events 一、touch 事件有哪些 页面中的 touch 事件一般在移动端使用&#xff0c;pc 端是没有效果的。 touch 相关的事件有四个 touc…

触摸事件(touchstart、touchmove和touchend)

触摸事件(touch)会在用户手指放在屏幕上面的时候、在屏幕上滑动的时候或者是从屏幕上移开的时候出发。下面具体说明 touchstart事件&#xff1a;当手指触摸屏幕时候触发&#xff0c;即使已经有一个手指放在屏幕上也会触发。 touchmove事件&#xff1a;当手指在屏幕上滑动的时候…

Vue 绑定使用 touchstart touchmove touchend

今天要做一个页面div长按后触发事件&#xff0c;简单学习后实现如下&#xff1a; 先看代码&#xff1a; <template><div><div class"test" touchstart"gtouchstart()" touchmove"gtouchmove()" touchend"gtouchend()"…

H5移动端 利用touchstart 达到长按事件

一开始的没加.native ,结果长按事件不会触发, 原因是: 给vue组件绑定事件时候&#xff0c;必须加上native &#xff0c;否则会认为监听的是来自Item组件自定义的事件等同于在子组件中: 子组件内部处理click事件然后向外发送click事件: semit"dlick”.fn) //长按事件&am…

javaScript-touch事件详解(touchstart、touchmove和touchend)-滑动事件案例

HTML5中新添加了很多事件&#xff0c;但是由于他们的兼容问题不是很理想&#xff0c;应用实战性不是太强&#xff0c;所以在这里基本省略&#xff0c;咱们只分享应用广泛兼容不错的事件&#xff0c;日后随着兼容情况提升以后再陆续添加分享。今天为大家介绍的事件主要是触摸事件…

「移动端」touch事件,touchEvent对象

随着智能手机普及&#xff0c;有越来越多的手机网页和网页版游戏&#xff0c;手机触摸、移动、旋转等等&#xff0c;多种操作。一般电脑的人机交互靠的是鼠标&#xff0c;而手机用的就是触摸。区别有&#xff1a; PC 端一个电脑只能有一个鼠标&#xff0c;而移动端有多点触摸。…

移动端开发touchstart,touchmove,touchend事件详解使用

页面使用到了触摸事件"touchstart"&#xff0c;“touchmove”&#xff0c;“touchend”。需求&#xff1a;当你手指接触到屏幕并向左滑动时&#xff0c;被滑动的服务商信息开始向左滑动同时出现"编辑"和"停用"操作按钮。若继续用手指向右滑动&am…

C++智能指针的实现与使用(详解)

C智能指针的实现与使用(详解) 智能指针介绍 缘起 C指针的实际问题 分配内存忘记释放&#xff0c;导致内存泄漏有指针引用时释放内存&#xff0c;导致其它指针非法访问异常一些对象的声明周期难以明确 办法 首先重载运算符*、->、[]、使得一个类具有指针的操作 其次&a…

详谈智能指针

一、什么是智能指针 在讲智能指针之前我们必须先知道什么是智能指针&#xff0c;智能指针是一个类&#xff0c;是对普通指针的一个封装使得智能指针对象具有普通指针类型一样的操作。 智能指针是存储指向动态分配&#xff08;堆&#xff09;对象指针的类&#xff0c;用于生…

C++中智能指针详解

1、问题引入 在C中&#xff0c;静态内存和栈内存外&#xff0c;还有一部分内存称为堆程序用堆来存储动态分配的对象即那些在程序运行时分配的对象&#xff0c;当动态对象不再使用时&#xff0c;我们的代码必须显式的销毁它们。在C中一般使用“new”&#xff1a;在动态内存中为对…

C/C++智能指针

目录 1.1RAII(资源获取几初始化) 1.2auto_ptr 1.3unique_ptr 1.4shared_ptr 1.5weak_ptr 我们在在动态开辟空间的时候&#xff0c;malloc出来的空间如果没有进行释放&#xff0c;那么回传在内存泄漏问题。或者在malloc与free之间如果存在抛异常&#xff0c;那么还是有内存泄…

Qt 之 智能指针汇总

来源 还有其他一些&#xff0c;做了一些汇总和测试&#xff0c;就不全列了。 文章目录&#xff1a; 一、垂悬指针的问题 二、Qt中的智能指针 1、QPointer 2、QSharedPointer & QWeakPointer 3、QScopedPointer 4、其他智能指针 三、实践记录 …