java集合框架

集合概述


  • 概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。

  • 和数组的区别:

  1. 数组长度固定,集合长度不固定。

  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。

  • 位置: java.util.*;

Collection体系集合


Collection父接口


  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。

  • 方法:

    • boolean add(Object obj) //添加一个对象。
    • boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
    • void clear() //清空此集合中的所有对象。
    • boolean contains(Object o) //检查此集合中是否包含o对象。
    • boolean equals(Object o) //比较此集合是否与指定对象相等。
    • boolean isEmpty() //判断此集合是否为空。
    • boolean remove(Object o) //在此集合中移除o对象。
    • int size() //返回此集合中的元素个数。
    • Object[] toArray() //姜此集合转换成数组。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    /**
    * Collection接口的使用(一)
    * 1.添加元素
    * 2.删除元素
    * 3.遍历元素
    * 4.判断
    */
    public class Demo1{
    pubic static void main(String[] args){
    //创建集合
    Collection collection=new ArrayList();
    // * 1.添加元素
    Collection.add("苹果");
    Collection.add("西瓜");
    Collection.add("榴莲");
    System.out.println("元素个数:"+collection.size());
    System.out.println(collection);
    // * 2.删除元素
    collection.remove("榴莲");
    System.out.println("删除之后:"+collection.size());
    // * 3.遍历元素
    //3.1 使用增强for
    for(Object object : collection){
    System.out.println(object);
    }
    //3.2 使用迭代器(迭代器专门用来遍历集合的一种方式)
    //hasnext();判断是否有下一个元素
    //next();获取下一个元素
    //remove();删除当前元素
    Iterator iterator=collection.Itertor();
    while(iterator.hasnext()){
    String object=(String)iterator.next();
    System.out.println(s);
    //删除操作
    //collection.remove(s);引发错误:并发修改异常
    //iterator.remove();应使用迭代器的方法
    // * 4.判断
    System.out.println(collection.contains("西瓜"));//true
    System.out.println(collection.isEmpty());//false
    }
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    /**
    * Collection接口的使用(二)
    * 1.添加元素
    * 2.删除元素
    * 3.遍历元素
    * 4.判断
    */
    public class Demo2 {
    public static void main(String[] args) {
    Collection collection=new ArrayList();
    Student s1=new Student("张三",18);
    Student s2=new Student("李四", 20);
    Student s3=new Student("王五", 19);
    //1.添加数据
    collection.add(s1);
    collection.add(s2);
    collection.add(s3);
    //collection.add(s3);可重复添加相同对象
    System.out.println("元素个数:"+collection.size());
    System.out.println(collection.toString());
    //2.删除数据
    collection.remove(s1);
    System.out.println("删除之后:"+collection.size());
    //3.遍历数据
    //3.1 增强for
    for(Object object:collection) {
    Student student=(Student) object;
    System.out.println(student.toString());
    }
    //3.2迭代器
    //迭代过程中不能使用collection的删除方法
    Iterator iterator=collection.iterator();
    while (iterator.hasNext()) {
    Student student=(Student) iterator.next();
    System.out.println(student.toString());
    }
    //4.判断和上一块代码类似。
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * 学生类
    */
    public class Student {
    private String name;
    private int age;
    public Student(String name, int age) {
    super();
    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 String toString() {
    return "Student [name=" + name + ", age=" + age +"]";
    }
    }

Collection子接口


List集合

  • 特点:有序、有下标、元素可以重复。

  • 方法:

    • void add(int index,Object o) //在index位置插入对象o。
    • boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
    • Object get(int index) //返回集合中指定位置的元素。
    • List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    /**
    * List子接口的使用(一)
    * 特点:1.有序有下标 2.可以重复
    *
    * 1.添加元素
    * 2.删除元素
    * 3.遍历元素
    * 4.判断
    * 5.获取位置
    */
    public class Demo3 {
    public static void main(String[] args) {
    List list=new ArrayList<>();
    //1.添加元素
    list.add("tang");
    list.add("he");
    list.add(0,"yu");//插入操作
    System.out.println("元素个数:"+list.size());
    System.out.println(list.toString());
    //2.删除元素
    list.remove(0);
    //list.remove("yu");结果同上
    System.out.println("删除之后:"+list.size());
    System.out.println(list.toString());
    //3.遍历元素
    //3.1 使用for遍历
    for(int i=0;i<list.size();++i) {
    System.out.println(list.get(i));
    }
    //3.2 使用增强for
    for(Object object:list) {
    System.out.println(object);
    }
    //3.3 使用迭代器
    Iterator iterator=list.iterator();
    while (iterator.hasNext()) {
    System.out.println(iterator.next());
    }
    //3.4使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。
    ListIterator listIterator=list.listIterator();
    //从前往后
    while (listIterator.hasNext()) {
    System.out.println(listIterator.next());
    }
    //从后往前(此时“遍历指针”已经指向末尾)
    while(listIterator.hasPrevious()) {
    System.out.println(listIterator.previous());
    }
    //4.判断
    System.out.println(list.isEmpty());
    System.out.println(list.contains("tang"));
    //5.获取位置
    System.out.println(list.indexOf("tang"));
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    /**
    * List子接口的使用(二)
    * 1.添加元素
    * 2.删除元素
    * 3.遍历元素
    * 4.判断
    * 5.获取位置
    */
    public class Demo4 {
    public static void main(String[] args) {
    List list=new ArrayList();
    //1.添加数字数据(自动装箱)
    list.add(20);
    list.add(30);
    list.add(40);
    list.add(50);
    System.out.println("元素个数:"+list.size());
    System.out.println(list.toString());
    //2.删除元素
    list.remove(0);
    //list.remove(20);很明显数组越界错误,改成如下
    //list.remove(Object(20));
    //list.remove(new Integer(20));
    System.out.println("元素个数:"+list.size());
    System.out.println(list.toString());
    //3-5不再演示,与之前类似
    //6.补充方法subList,返回子集合,含头不含尾
    List list2=list.subList(1, 3);
    System.out.println(list2.toString());
    }
    }

List实现类

ArrayList【重点】
  • 数组结构实现,查询块、增删慢;
  • JDK1.2版本,运行效率快、线程不安全。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/**
* ArrayList的使用
* 存储结构:数组;
* 特点:查找遍历速度快,增删慢。
* 1.添加元素
* 2.删除元素
* 3.遍历元素
* 4.判断
* 5.查找
*/
public class Demo5 {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList<>();
//1.添加元素
Student s1=new Student("唐", 21);
Student s2=new Student("何", 22);
Student s3=new Student("余", 21);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println("元素个数:"+arrayList.size());
System.out.println(arrayList.toString());
//2.删除元素
arrayList.remove(s1);
//arrayList.remove(new Student("唐", 21));
//注:这样可以删除吗(不可以)?显然这是两个不同的对象。
//假如两个对象属性相同便认为其是同一对象,那么如何修改代码?
//3.遍历元素
//3.1使用迭代器
Iterator iterator=arrayList.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
//3.2使用列表迭代器
ListIterator listIterator=arrayList.listIterator();
//从前往后遍历
while(listIterator.hasNext()) {
System.out.println(listIterator.next());
}
//从后往前遍历
while(listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
//4.判断
System.out.println(arrayList.isEmpty());
//System.out.println(arrayList.contains(new Student("何", 22)));
//注:与上文相同的问题。
//5.查找
System.out.println(arrayList.indexOf(s1));
}
}

注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public boolean equals(Object obj) {
//1.是否为同一对象
if (this==obj) {
return true;
}
//2.判断是否为空
if (obj==null) {
return false;
}
//3.判断是否是Student类型
if (obj instanceof Student) {
Student student=(Student) obj;
//4.比较属性
if(this.name.equals(student.getName())&&this.age==student.age) {
return true;
}
}
//不满足,返回false
return false;
}
ArrayList源码分析
  • 默认容量大小:private static final int DEFAULT_CAPACITY = 10;

  • 存放元素的数组:transient Object[] elementData;

  • 实际元素个数:private int size;

  • 创建对象时调用的无参构造函数:

    1
    2
    3
    4
    5
    //这是一个空的数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?

    这就得看看add方法的源码了:

    1
    2
    3
    4
    5
    public boolean add(E e) {
    ensureCapacityInternal(size + 1); // Increments modCount!!
    elementData[size++] = e;
    return true;
    }

    假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到ensureCapacityInternal(size + 1);该方法源码如下:

    1
    2
    3
    private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

    该方法中的参数minCapacity传入的值为size+1也就是 1,接着我们再进入到calculateCapacity(elementData, minCapacity)里面:

    1
    2
    3
    4
    5
    6
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
    }

    上文说过,elementData就是存放元素的数组,当前容量为0,if条件成立,返回默认容量DEFAULT_CAPACITY也就是10。这个值作为参数又传入ensureExplicitCapacity()方法中,进入该方法查看源码:

    1
    2
    3
    4
    5
    6
    private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
    grow(minCapacity);
    }

    我们先不要管modCount这个变量。

    因为elementData数组长度为0,所以if条件成立,调用grow方法,重要的部分来了,我们再次进入到grow方法的源码中:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
    }

    这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为oldCapacity+一个增量,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:

    1
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    这个值太大了以至于第二个if条件没有了解的必要。

    最后一句话就是为elementData数组赋予了新的长度,Arrays.copyOf()方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。

    这时候再回到add的方法中,接着就向下执行elementData[size++] = e;到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。

Vector
  • 数组结构实现,查询快、增删慢;

  • JDK1.0版本,运行效率慢、线程安全。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    /**
    * Vector的演示使用
    *
    *1.添加数据
    *2.删除数据
    *3.遍历
    *4.判断
    */
    public class Demo06 {
    public static void main(String[] args) {
    Vector vector = new Vector();
    //1.添加元素
    vector.add("苹果");
    vector.add("草莓");
    vector.add("西瓜");
    //2。遍历
    //for 或 增强for都行
    //使用for
    System.out.println("------使用for------");
    for (int i = 0; i <vector.size() ; i++) {
    System.out.println(vector.get(i));
    }
    //增强for
    System.out.println("------增强for------");
    for (Object o : vector) {
    System.out.println(o);
    }
    //迭代器
    System.out.println("------迭代器------");
    Iterator i = vector.iterator();
    while(i.hasNext()){
    System.out.println(i.next());
    }
    //枚举器
    System.out.println("------枚举器------");
    Enumeration en = vector.elements();
    //3.判断
    vector.contains("苹果");
    //4.其他方法
    System.out.println(vector.firstElement());
    //.....
    }
    }
     

LinkedList
  • 链表结构实现,增删快,查询慢。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
/**
* LinkedList的用法
* 存储结构:双向链表
* 1.添加元素
* 2.删除元素
* 3.遍历
* 4.判断
*/
public class Demo2 {
public static void main(String[] args) {
LinkedList linkedList=new LinkedList<>();
Student s1=new Student("唐", 21);
Student s2=new Student("何", 22);
Student s3=new Student("余", 21);
//1.添加元素
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
linkedList.add(s3);
System.out.println("元素个数:"+linkedList.size());
System.out.println(linkedList.toString());
//2.删除元素
/*
* linkedList.remove(new Student("唐", 21));
* System.out.println(linkedList.toString());
*/
//3.遍历
//3.1 使用for
for(int i=0;i<linkedList.size();++i) {
System.out.println(linkedList.get(i));
}
//3.2 使用增强for
for(Object object:linkedList) {
Student student=(Student) object;
System.out.println(student.toString());
}
//3.3 使用迭代器
Iterator iterator =linkedList.iterator();
while (iterator.hasNext()) {
Student student = (Student) iterator.next();
System.out.println(student.toString());
}
//3.4 使用列表迭代器(略)
//4. 判断
System.out.println(linkedList.contains(s1));
System.out.println(linkedList.isEmpty());
System.out.println(linkedList.indexOf(s3));
}
}
LinkedList源码分析

LinkedList首先有三个属性:

  • 链表大小:transient int size = 0;
  • (指向)第一个结点/头结点: transient Node<E> first;
  • (指向)最后一个结点/尾结点:transient Node<E> last;

关于Node类型我们再进入到类里看看:

1
2
3
4
5
6
7
8
9
10
11
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

首先item存放的是实际数据;next指向下一个结点而prev指向上一个结点。

Node带参构造方法的三个参数分别是前一个结点、存储的数据、后一个结点,调用这个构造方法时将它们赋值给当前对象。

LinkedList是如何添加元素的呢?先看看add方法:

1
2
3
4
public boolean add(E e) {
linkLast(e);
return true;
}

进入到linkLast方法:

1
2
3
4
5
6
7
8
9
10
11
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}

假设刚开始new了一个LinkedList对象,first和last属性都为空,调用add进入到linkLast方法。

首先创建一个Node变量 l 将last(此时为空)赋给它,然后new一个newNode变量存储数据,并且它的前驱指向l,后继指向null;再把last指向newNode。如下图所示:

如果满足if条件,说明这是添加的第一个结点,将first指向newNode:

至此,LinkedList对象的第一个数据添加完毕。假设需要再添加一个数据,我们可以再来走一遍,过程同上不再赘述,图示如下:


ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。

 

posted @   Blululue  阅读(33)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示