集合原理
一、List的执行原理
要解决的痛点:
1.数组的目前唯一的数据批量存储的手段
2.数组的扩容效率极慢(必须要解决的问题)
解决痛点问题:
1.暂时没有其他策略之前,先丰富其功能
2.如果每扩容一次都需要消耗大量的时间,可以减少其扩容的次数;通过牺牲空间,将扩容所消耗的时间集中在某几个特定的时间节点
确定需求主体
package com.mine.demo01;
public interface List {
/**
* 添加元素
* @param item
*/
public void add(Object item);
/**
* 获取集合长度
* @return
*/
public int size();
/**
* 获取元素当前位置
* @param item
* @return
*/
public int indexOf(Object item);
/**
* 获取元素最后所在位置
* @param item
* @return
*/
public int lastIndexOf(Object item);
/**
* 根据游标获取元素
* @param index
* @return
*/
public Object get(int index);
/**
* 根据游标修改元素
* @param index
*/
public void set(int index,Object item);
/**
* 根据游标删除某个元素
* @param index
*/
public void remove(int index);
}
二、ArrayList的实现
扩容策略:
1.默认空间长度为10
2.数据长度和空间长度分开治理
3.每次需要扩容的时候,扩容的长度为原本长度的一半
优势:
因为是基于数组,所以检索效率极高
劣势:
1.因为空间换时间,所以在特定节点上,依然存在扩容的时间大量消耗。同时,空间的浪费是不可避
免。增减效率在特定节点上不高。
2.空间有限
1.初始化默认长度
//1.初始化默认长度
private Object[] items;
//默认集合初始长度
public static final int DEFAULT_VALUE_LENGTH = 10;
//最大扩容长度
public static final int MAX_VALUE_LENGTH = Integer.MAX_VALUE - 8;
public Scanner scanner;
//初始化
public ArrayList(){
this(DEFAULT_VALUE_LENGTH);
}
public ArrayList(int defaultLength){
this.items = new Object[defaultLength];
}
2.初始化游标
private int length = 0; //初始的集合元素个数
3.判断游标是否存在越界
//3.判断游标是否越界
public boolean checkIndexBorder(int index){
int maxIndex = this.length - 1;
if(index < 0 || index > maxIndex){
throw new RuntimeException("游标越界:"+ index);
}
return true;
}
4.空间扩容
//4.空间扩容
public void spaceExpansion(){
int nowLength = this.items.length;//当前元素个数
int newLength = nowLength + (nowLength >> 1);//要扩容后的新的数组个数,>>为除2
//扩容的上限
if(newLength > MAX_VALUE_LENGTH){
newLength = MAX_VALUE_LENGTH;
}
Object[] newItems = new Object[newLength];//新的数组
System.arraycopy(this.items,0,newItems,0,nowLength);//将旧数组的元素移到新数组
this.items = newItems;//把新的数组,赋值给旧的数组
}
public void spaceExpansion2(){//另一种写法
int nowLength = this.items.length;//当前元素个数
int newLength = nowLength + (nowLength >> 1);//要扩容后的新的数组个数,>>为除2
//扩容的上限
if(newLength > MAX_VALUE_LENGTH){
newLength = MAX_VALUE_LENGTH;
}
this.items = Arrays.copyOf(this.items,newLength);
}
5.添加元素
判断是否要扩容的依据是数据长度小于空间长度,如果数据长度等于空间长度,说明要先扩容,再加数据
//5.添加元素
@Override
public void add(Object item) {
//判断集合是否满了
if(this.length == MAX_VALUE_LENGTH){
throw new RuntimeException("集合已满!");
}
//判断要不要扩容
if(this.length == this.items.length){
this.spaceExpansion();//扩容
}
//往末尾添加元素
this.items[this.length] = item;
this.length++;//数据长度迭代
}
5.元素的长度和获取
//6.元素的长度和获取
@Override
public int size() {
return this.length;
}
6.ArrayList.java
package com.mine.demo01;
import java.util.Arrays;
import java.util.Scanner;
public class ArrayList implements List{
//1.初始化默认长度
private Object[] items;
//默认集合初始长度
public static final int DEFAULT_VALUE_LENGTH = 10;
//最大扩容长度
public static final int MAX_VALUE_LENGTH = Integer.MAX_VALUE - 8;
public Scanner scanner;
//初始化
public ArrayList(){
this(DEFAULT_VALUE_LENGTH);
}
public ArrayList(int defaultLength){
this.items = new Object[defaultLength];
}
//2.初始化游标
private int length = 0;//初始的集合元素个数
//3.判断游标是否越界
public boolean checkIndexBorder(int index){
int maxIndex = this.length - 1;
if(index < 0 || index > maxIndex){
throw new RuntimeException("游标越界:"+ index);
}
return true;
}
//4.空间扩容
public void spaceExpansion(){
int nowLength = this.items.length;//当前元素个数
int newLength = nowLength + (nowLength >> 1);//要扩容后的新的数组个数,>>为除2
//扩容的上限
if(newLength > MAX_VALUE_LENGTH){
newLength = MAX_VALUE_LENGTH;
}
Object[] newItems = new Object[newLength];//新的数组
System.arraycopy(this.items,0,newItems,0,nowLength);//将旧数组的元素移到新数组
this.items = newItems;//把新的数组,赋值给旧的数组
}
public void spaceExpansion2(){//另一种写法
int nowLength = this.items.length;//当前元素个数
int newLength = nowLength + (nowLength >> 1);//要扩容后的新的数组个数,>>为除2
//扩容的上限
if(newLength > MAX_VALUE_LENGTH){
newLength = MAX_VALUE_LENGTH;
}
this.items = Arrays.copyOf(this.items,newLength);
}
//5.添加元素
@Override
public void add(Object item) {
//判断集合是否满了
if(this.length == MAX_VALUE_LENGTH){
throw new RuntimeException("集合已满!");
}
//判断要不要扩容
if(this.length == this.items.length){
this.spaceExpansion();//扩容
}
//往末尾添加元素
this.items[this.length] = item;
this.length++;//数据长度迭代
}
//6.元素的长度和获取
@Override
public int size() {
return this.length;
}
@Override
public int indexOf(Object item) {
for(int i = 0 ; i < this.length ; i ++){
if(item!=null && item.equals(this.items[i])){
return i;
}else if(this.items[i] != null && this.items[i].equals(item)){
return i;
}else if(this.items[i] == item){
return i;
}
}
return -1;
}
@Override
public int lastIndexOf(Object item) {
for(int i = this.length-1 ; i >= 0 ; i --){
if(item!=null && item.equals(this.items[i])){
return i;
}else if(this.items[i] != null && this.items[i].equals(item)){
return i;
}else if(this.items[i] == item){
return i;
}
}
return -1;
}
@Override
public Object get(int index) {
//验证游标是否越界
if(this.checkIndexBorder(index)){
return this.items[index];
}
return null;
}
@Override
public void set(int index,Object item) {
if(this.checkIndexBorder(index)){
this.items[index] = item;
System.out.println("修改成功");
}
}
@Override
public void remove(int index) {
if(this.checkIndexBorder(index)){
Object[] newItems = new Object[this.items.length];
// for(int i = 0;i < index;i++){
// newItems[i] = this.items[i];
// }
// for(int i = index +1 ;i < this.items.length-1;i++){
// newItems[i] = this.items[i];
// }
// this.items = newItems;
System.arraycopy(this.items,0,newItems,0,index);
System.arraycopy(this.items,index+1,newItems,index,this.length-index-1);
}
System.out.println(Arrays.toString(this.items));
}
public void checkItem(Object item){
int index = -1;
for(int i = 0;i < this.length; i++){
if(this.items[i].equals(item)){
index = i;
break;
}
}
System.out.println("游标是:"+index);
}
}
三、链表的运用和开发:LinkedList
1.什么是双向链表
运用指针原理,在java当中即对象的内存地址。让对象和对象之间能够相互寻找前一位或者后一位
存在的意义:和ArrayList的性能进行互补
优势:增减元素效率极高
缺点:随机遍历效率不高,每次都要从头找
1.单个元素(节点)的设计:Node
//一个内部类
public class Node {
Node proe;//前一位(属性)
Object item;//真正保存集合元素的属性
Node next;//下一位(属性)
}
2.开始和结束的位置
//开始的位置
private Node first;
//结束的位置
private Node last;
//元素的个数
private int length;
3.添加首尾元素
public void addFirst(Object obj){
Node node = new Node();
node.item = obj; //要添加的元素
if(this.first == null){//假定为第一次添加
this.first = node;
this.last = node;
}else {
node.next = this.first; //现在的首位,变成新建节点的下一位
this.first.proe = node;//现在首位的上一位变成新建节点
this.first = node; //现在的首位变成新建节点
}
this.length++;//元素长度迭代
}
public void addLast(Object obj){
Node node = new Node();
node.item = obj; //要添加的元素
if(this.last == null){
this.first = null;
this.last = null;
}else {
node.proe = this.last;
node.proe.next = node;
this.last = node;
}
this.length++;
}
4.链表的遍历取值
public Object get(int index) {
//判断游标是否越界
if(this.checkIndexBorder(index)){
int i = 0;
Node node = this.first;
while (node.next != null & i < index){
node = node.next;
i++;
}
//循环结束,保存是是要检索的值
return node.item;
}
return null;
}
5.修改
public void set(int index, Object item) {
//判断游标是否越界
if(this.checkIndexBorder(index)){
int i = 0;
Node node = this.first;
while (node.next != null && i < index){
node = node.next;
i++;
}
node.item = item;
}
}
6.获取首位的元素和删除首位元素
public Object getFirst(){
return this.first.item;
}
public Object getLast(){
return this.last.item;
}
public void removeFirst(){
//找到第二个
Node sen = this.first.next;
//将第二个的上一个位置置空
sen.proe = null;
//将first指向第二个位置
this.first = sen;
//修改长度
this.length--;
}
public void removeLast(){
//找到倒数第二个
Node sen = this.last.proe;
//将倒数第二个的下一个位置置空
sen.next = null;
//将last指向倒数第二个
this.last = sen;
//修改长度
this.length--;
}
7.LinkedList.java
package com.mine.demo01;
public class LinkedList1 implements List{
private class Node{
Node proe;//前一位(属性)
Object item;//真正保存集合元素的属性
Node next;//下一位(属性)
}
//开始的位置
private Node first = null;
//结束的位置
private Node last = null;
//元素的个数
private int length = 0;
public void addFirst(Object obj){
Node node = new Node();
node.item = obj;//要添加的元素
if(this.first == null){//假定为第一次添加
this.first = node;
this.last = node;
}else{
node.next = this.first;//现在的首位,变成新建节点的下一位
this.first.proe = node;//现在首位的上一位变成新建节点
this.first = node;//现在的首位变成新建节点
}
this.length++;//迭代元素长度
}
public void addLast(Object obj){
Node node = new Node();
node.item = obj;//要添加的元素
if(this.last == null){//假定为第一次添加
this.first = node;
this.last = node;
}else{
node.proe = this.last;
this.last.next = node;
this.last = node;
}
this.length++;//迭代元素长度
}
public Object getFirst(){
return this.first.item;
}
public Object getLast(){
return this.last.item;
}
public void removeFirst(){
if(this.length >= 1){
//1.找到第二个
Node sen = this.first.next;
//2.把第二个的上一位置空
sen.proe = null;
//3.把first指向原第二位
this.first = sen;
//4.修改长度
this.length --;
}
}
public void removeLast(){
if(this.length >= 1){
//1.找到倒数第二个
Node sen = this.last.proe;
//2.把第二个的上一位置空
sen.next = null;
//3.把first指向原第二位
this.last = sen;
//4.修改长度
this.length --;
}
}
@Override
public void add(Object item) {
this.addLast(item);
}
@Override
public int size() {
return this.length;
}
@Override
public int indexOf(Object item) {
int i = 0;
Node node = this.first;//链表的头,对应的游标为0
//循环的条件是看当前节点是否有下一位
while(node.next != null){
if(item!=null && item.equals(node.item)){
return i;
}else if(node.item != null && node.item.equals(item)){
return i;
}else if(node.item == item){
return i;
}
node = node.next;
i ++;
}
//如果没找到给-1
return -1;
}
@Override
public int lastIndexOf(Object item) {
int i = this.length - 1;
Node node = this.last;//链表的尾部,对应的游标为length - 1
//循环的条件是看当前节点是否有上一位
while(node.proe != null){
if(item!=null && item.equals(node.item)){
return i;
}else if(node.item != null && node.item.equals(item)){
return i;
}else if(node.item == item){
return i;
}
node = node.proe;
i --;
}
//如果没找到给-1
return -1;
}
@Override
public Object get(int index) {
//验证是否越界
if(this.checkIndexBorder(index)){
int i = 0;
Node node = this.first;//链表的头,对应的游标为0
//循环的条件是看当前节点是否有下一位
while(node.next != null && i < index){
node = node.next;
i ++;
}
//循环结束,保存的是要检索到的节点
return node.item;
}
return null;
}
@Override
public void set(int index, Object item) {
//验证是否越界
if(this.checkIndexBorder(index)){
int i = 0;
Node node = this.first;//链表的头,对应的游标为0
//循环的条件是看当前节点是否有下一位
while(node.next != null && i < index){
node = node.next;
i ++;
}
//循环到指定位置停止
node.item = item;
}
}
@Override
public void remove(int index) {
//验证是否越界
if(this.checkIndexBorder(index)){
int i = 0;
Node node = this.first;//链表的头,对应的游标为0
//循环的条件是看当前节点是否有下一位
while(node.next != null && i < index){
node = node.next;
i ++;
}
//循环到指定位置停止
Node p = node.proe;
Node n = node.next;
n.proe = p;
p.next = n;
//修改长度
this.length --;
}
}
/**
* 判断游标是否越界
*/
private boolean checkIndexBorder(int index){
int maxIndex = this.length - 1;
if(index < 0 || index > maxIndex){
throw new RuntimeException("游标越界:"+index);
}
return true;
}
}