Collection集合
概述:
在我们之前学习Java基础时我们是用数组来存放同类型的多个数据,数据类型既可以是基数类型,也可以是类类型。而Java类库中还有能存放多个数据的容器,就是集合(Collection),Collection是集合的父接口,继承Collection的子接口分别有List(java.util.List)和Set(java.util.Set),List的实现类中常用的分别是ArrayList和Linkedlist,Set实现类中HashSet则为最常用的。
集合与数组的区别
1. 集合元素数据类型全部都是引用类型,数组元素可以是基数类型或引用类型
2. 集合的容量是可变的,始终等于当前元素个数;数组一经定义容量(长度)不可改变
3. 集合中定义了更多的方法,且集合元素存储结构不都是顺序存储(LinkedList就是链式存储,HashSet不能保证数据都是连续存储的);数组只能是顺序存储
常用函数
正是因为Collection是所有集合的父接口,List与Set集合中通用的功能集成于此
public boolean add(E e):在集合的末尾的位置添加新的元素(如果参数传的是基数类型,会自动装箱变成包装类对象)
public void clear():一次性清空集合中全部元素
public boolean remove(E e):按照元素名删除指定元素
public boolean contains(Object obj):判断集合里面有无要找的元素
public boolean isEmpty():判断集合是否为空
public int size():相当于字符串的length()函数,返回集合中元素个数
public Object[] toArray():把集合元素依次存放到对象数组中,数组的长度等于当前
增强for循环(foreach)
代码格式
for (元素数据类型 变量名 : 集合/数组名)
Tips:idea中输入容器名.for可以快速生成foreach
用途
遍历整个容器,即访问容器中的所有元素,小括号中的变量名代表容器中当前正访问的元素,可利用这个变量名对当前访问的元素进行操作。
与for循环遍历容器的区别
foreach相比for循环,遍历容器中元素看起来更直观了,但是for循环可以指定扫描容器的区间(即指定遍历元素的起止位置),foreach只能全盘扫描。
List接口
作为Collection的子接口,除了具备Collection里面所有的功能以外,也有自身的特性:
1. 元素内容可重复
2. 元素有前后逻辑顺序
3. 元素之间有了逻辑顺序就会有索引
正是因为元素有索引,我们可以更自由地完成增删改查的操作:
增:public void add(int index, E element) 将一个新的元素放在指定逻辑位置
public void add(E e) 继承了父接口的add(),将新元素放到逻辑位置的最后一位,也就是所谓的“尾插法”
删:public E remove(int index) 按照逻辑位置(索引)删除元素,返回的是本次删除的元素
改:public E set(int index, E element) 修改指定逻辑位置的元素,返回修改前的元素
查:public E get(int index) 查找指定逻辑位置的元素,返回查找到的元素
Warning:增删改查只要涉及到index参数时千万注意,index必须介于0到原集合长度-1之间,否则会出现索引越界异常
ArrayList集合
概述
ArrayList是List的一个实现类,可以理解为长度可变的数组,长度始终与当前元素个数相等,相当于数据结构中的顺序线性表,即逻辑位置上相邻的元素,存储位置也是相邻的。这样的存储结构优缺点极其明显,其优点在于“可以通过索引直接查找表中任意元素,方便读取”,对应的缺点就是插入和删除元素时,需要移动大量的其余元素,尤其是删除第一个元素时需要有(集合长度 - 1)个元素前移。
使用场景
当一组数据无需频繁增删但经常需要查看时可以用ArrayList存储
LinkedList集合
概述
LinkedList也是List的一个实现类,顾名思义就是链表,链表应用了线性表中链式存储的方式,即在逻辑上相邻的元素,存储位置上不一定相邻,精确来讲是带头结点和尾结点的双向链表,在LinkedList类定义中有这样一个片段
这也就意味着结点不仅包含数据本身,而且包含存放前一个和后一个结点的指针(虽然Java没有星号标识指针,但引用类型统统可以理解为指针)。所以增删元素时,只需要修改相邻元素的next和prev指针即可。而想要查找元素时虽然有现成的方法,但实际上要通过头结点或尾结点顺藤摸瓜才能找到想要的元素。
使用场景
当一组数据不稳定需要频繁增删时可以用LinkedList存储,这样不用像顺序表一样每次改动数据都要移动较多的数据。
特色功能
在本Reno学C语言版数据结构时,顺序表、线性链表、队列、栈全部都要自己定义,今天认识了LinkedList咱直接用现成的方法,因为LinkedList集合的数据结构是双向线性链表,可以同时被当作队列(先进先出)和栈(后进先出)。
入队列/出队列操作:
public void addFirst(E e)/public E removeLast()或
public void addLast(E e)/public E removeFirst()
压栈/弹栈操作:
public void push(E e)/public E pop()
我们借个示例研究下压栈和弹栈操作究竟是对链表的哪个位置的元素进行操作
import java.util.LinkedList;
public class LinkedListExec {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("CPU0");
list.add("CPU1");
list.add("CPU2");
list.add("CPU3");
list.pop();
list.push("CPU4");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
输出如下
瞅了一眼pop()和push()方法的定义
发现两个方法都是对第一个元素下手,都已经有addFirst()和removeFirst()为何还要定义push()和pop()呢?原因不在于这些方法本身,而是突出栈结构的特点。
总结:
1. 集合是功能上优于数组的容器,虽然只能存引用类型,但可以利用包装类的自动装箱把基数类型的数据转换为引用类型。
2. foreach遍历数组确实非常方便,但当需要访问部分区间的元素或者循环加入新元素时是不能用foreach循环的。
3. 在学习ArrayList和LinkedList时本Reno又回想起数据结构C语言版关于线性表的知识,对于线性表,我们除了学习如何操作线性表之外,还需要知道不同线性表的使用场景。
4. 对于ArrayList和LinkedList,重点不在于里面方法是如何定义的,而是在于它们的逻辑和存储结构是什么样的,ArrayList由于元素存储空间是连续的可以由索引直接访问里面的元素,同时方便直接修改指定元素的内容;LinkedList由于每个元素虽然在存储空间上不一定连续,但每个元素都存了相邻元素的指针,增删元素仅需修改这些指针的指向即可。
5. 对于根据索引进行的操作要谨防系统抛出索引越界异常。