集合类(容器)
集合可以理解为动态数组,但是他区别于传统数组,
传统数组,是静态、线性的,长度是固定的。传统数组是通过下标控制元素的,数组的线性顺序是由下标决定的。
动态数组,顾名思义,是可以改变的数组,改变长度。可实现若干个对象的存储。
- 传统数组长度定下了就不能变。如果以后需要在此数组中新增元素,或者是需要删除元素,大大受限制。数组长度已经固定,也就是说明分配空间固定,即内存空间给定了大小。
增加元素:新增的元素如果大于此长度,将出现ArrayIndexOutOfBoundsException异常,不能正常运行。
删除元素:某一处元素为空指向状态,没有指针指向它,这是删除原理。并不是将那一个位置的元素修改为null就等于删除。
而传统数组是做不到有空指向的。
因此,从JDK1.2引入了集合类
JDK1.8实现算法改进
集合类概述
集合类主要是对常见的数据结构进行完整的实现包装,然后提供了一系列的接口和实现子类。依次来帮助用户减少数据结构所带来的开发困难。
所有的类都继承Object,集合类也是。
List集合
List集合继承于Collection(单值集合操作最大父接口)
List集合特点
- 元素允许重复
- 各元素的顺序就是插入对象的顺序
List接口的实现类
- ArrayList类(List集合使用中占90%)
优点:实现了可变数组,允许保存所有元素,包括null。可以根据索引位置对集合进行快速的随机访问
缺点:指向索引位置插入对象或删除对象的速度较慢。
2.LinkedList类(占比8%)
采用链表结构保存对象(链表的顺序是由各个对象里的指针决定的,即通过指针前后指向相连,而不能像数组直接根据脚标直接选择指定对象)。
优点:这样的结构优点是便于向集合中插入、删除对象,效率高
缺点:对于随机访问集合中的对象,使用LinkedList类实现List集合的效率低。(链表只能通过前后相邻的对象指向才能找到,因此如果时查询一个对象,可能需要遍历整个集合,时间复杂度为O(n))
3.Vector类(占比2%)——转载
Vector 是同步访问的
Vector 包含了许多传统的方法,这些方法不属于集合框架。
Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。
Vector 类支持 4 种构造方法。
详见:https://www.runoob.com/java/java-vector-class.html.
根据各自的优缺点,我认为可以根据各自的优点,在需求不同的时候使用。例如ArrayList访问快,那么在查询数据的时候,那么这一方面多半都适合用ArrayListArrayList。而LinkedList用于修改等。
ArrayList子类继承结构
举例
import java.util.ArrayList;
import java.util.List;
public class Test {
public static void main (String args[]) {
List<String> all = new ArrayList<String>();
all.add("ab");
all.add("cd");
all.add("ef");
System.out.println(all);
System.out.println(all.get(0)); //集合的索引同样也是从0开始。
}
}
Java没有指针,实现链表练习代码
interface ILink<E> { //设置泛型避免安全隐患
public void add(E e); //增加数据
public int size(); //获取数据的个数
public void delete(E e); //输出链表数据
public boolean isEmpty(); //判断是否为空集合
public Object toArray(); //获得集合的数据
public E get(int index); //获取对应索引的数据
public void set(int index ,E data); //根据给定的索引,修改该索引的数据
public boolean contains(E data); //判断数据书否存在
public void clean(); //集合清空
}
class LinkImpl<E> implements ILink<E>{
//——————————————————————LinkImpl内部类——————————————————————————————
private class Node{
private E data; //所需保存的数据
private Node next; //保存下一个引用
private Object returnData[];
public Node(E data) { //存储数据
this.data = data;
}
//连接外部,实现内容增加
public void addNode(Node newNode) { //判断当前所在节点
if(this.next == null) {
this.next = newNode; //将下一个节点指向添加的节点元素
}else {
this.next.addNode(newNode);
//如果下一个节点有元素了,继续向后递归调用此方法,直到下一个节点为空,然后添加元素
}
}
//连接外部,实现集合内容获取
public void toArrayNode() {
LinkImpl.this.returnData[LinkImpl.this.foot ++] = this.data ; //将每个节点的数据依次存入数组,遇到空元素时停止存入
if(this.next != null) { //当下一个节点还有元素时
this.next.toArrayNode(); //递归存入
}
}
//连接外部,实现指定索引的内容获取
public E getNode(int index) {
if(LinkImpl.this.foot ++ == index) { //根据脚标是否相等,返回相应元素
return this.data;
} else {
return this.next.getNode(index); //脚标不相等,递归,脚标向后指
}
}
//连接外部的set方法,实现内容修改
public void setNode(int index ,E data) { //类似getNode 方法
if(LinkImpl.this.foot ++ == index) {
this.data = data;
} else {
this.next.setNode(index ,data);
}
}
//连接外部的contains方法,实现内容查询
public boolean containsNode(E data) {
if(this.data.equals(data)) {
return true;
}else {
if(this.next == null) {
return false;
}else {
return this.next.containsNode(data); //递归,直到内容相同
}
}
}
//连接外部,实现指定内容的删除
public void deleteNode(Node prev ,E data) {
if(this.data.equals(data)) {
prev.next = this.next;
} else {
if(this.next != null) {
this.next.deleteNode(this, data); //向后继续删除
}
}
}
}
//————————————————————————Link类中定义的成员和方法————————————————————
private Node root; //保存根元素
private int count; //次数
private Object[] returnData; //数组存储信息
private int foot ; //存储脚标
public void add(E e) { //添加数据方法
if(e == null) { //如果添加的没有数据,则结束方法
return ;
}
//如果添加的有数据,则开始检测
Node newNode = new Node(e);
//首节点没有数据时,此时已经将调用该方法的数据赋予给临时内部类对象newNode
if(this.root == null) { //首节点为空时,即此时只有空的表头,应该先将表头给值。
this.root = newNode; //将第一个节点作为根节点
}
else {
this.root.next = newNode;
}
this.count ++; //调用多少次add方法即增加多少数据
}
public int size() { //获得数据个数
return count;
}
public boolean isEmpty() { //返回是否为空集合判断
return this.root == null;
}
public Object[] toArray() { //获取集合信息
if(this.isEmpty()) {
return null;
}else {
this.foot = 0 ;
this.returnData = new Object[this.count] ; //根据现在已有额长度开辟一个数组
//利用Node类进行数据递归存取
this.root.toArrayNode();
return this.returnData;
}
}
public E get(int index) { //根据索引输出元素
if(index > this.count) {
return null;
} //索引类的数据获取应该由Node类完成
this.foot = 0;
return this.root.getNode(index);
}
//修改指定索引数据
public void set(int index ,E data) {
if(index >= this.count) {
return;
}
this.foot = 0;
this.root.setNode(index, data);
}
//判断数据是否存在
public boolean contains(E data) {
if(data == null)
return false;
return this.root.containsNode(data);
}
//删除集合元素
public void delete(E data) {
if(this.contains(data)) {
if(this.root.data.equals(data)) {
this.root = this.root.next;
} else {
this.root.next.deleteNode(this.root, data);
//递归调用内部类删除方法,下一个继续查询,直到有与data内容相同的元素
}
this.count --;
}
}
//集合清空
public void clean() {
this.root = null;
this.count = 0;
}
}
public class Test{
public static void main(String[] args) {
//
LinkImpl<String> out = new LinkImpl<String>();
System.out.println("数据集合创建完毕");
System.out.println("数据添加之前拥有个数:" + out.size() + "\t空集合?" + out.isEmpty());
out.add("数据一");
out.add("数据二");
System.out.println("添加后拥有数据个数:" + out.size() + "\t空集合?" + out.isEmpty());
//修改第二个数据为666
out.set(1, "666");
//输出集合所有数据
System.out.println("数据集合:");
{
Object res[] = out.toArray();
for(Object obj : res) {
System.out.println(obj);
}
}
//获取集合第一个数据
System.out.println("集合第一个数据:" + out.get(1));
//查询是否有“六零五”这一个数据
System.out.println("数据“六零五”是否存在?" + out.contains("六零五"));
//删除数据一
out.delete("数据一");
System.out.println("删除数据一后的集合:");
{
Object res[] = out.toArray();
for(Object obj : res) {
System.out.println(obj);
}
}
out.clean(); //已清空集合
System.out.println(out.get(0));
}
}
运行结果