文章目录

  • 一、数组封装
  • 1.需求
  • 2.编码实现
  • 3.测试
  • 二、集合
  • 1.概述
  • 2.继承体系
  • 3.Collection
  • 3.1.常用方法
  • 3.2.使用方式
  • 3.3.Iterator
  • 4.forEach
  • 5.List
  • 5.1.ArrayList
  • 5.2.LinkedList
  • 5.2.1.个人理解
  • 5.2.2.基本使用
  • 5.2.3.底层实现
  • 5.2.3.1.节点类Node
  • 5.2.3.2.LinkedList
  • 5.2.3.3.添加-add
  • 5.2.3.4.获取-get
  • 5.2.3.5.删除原理


一、数组封装

1.需求

数组没有提供方法,操作比较繁琐,所以适当进行封装,提高开发效率、降低使用难度

2.编码实现

public class Array {
	private Object[] elements=new Object[10];
	//元素个数
	private int size=0;
	//根据索引获取数据
	public Object get(int index){
		return elements[index];
	}
	//根据索引修改数据
	public void set(int index,Object element){
		elements[index]=element;
	}
	//获取数组元素个数
	public int size(){
		return size;
	}
	//添加数据
	public void add(Object element){
		//判断原数组是否有空间
		if (size==elements.length) {
			//新数组扩容为原数组的二倍
			Object[] newArr=new Object[elements.length*2];
			//将原数组数据拷贝到新数组
			System.arraycopy(elements, 0, newArr, 0, size);
			elements=newArr;
		}
		elements[size]=element;
		size++;
	}
	//删除数据
	public void remove(int index){
		for (int i = index; i < size; i++) {
			 elements[i]=elements[i+1];
		}
		size--;
	}
}

3.测试

public class Test {
	public static void main(String[] args) {
		Array array=new Array();
		//尾部添加
		array.add(6);
		array.add(66);
		array.add(666);
		array.add(6666);
		array.add(66666);
		//查询
		System.out.println(array.get(2));
		//修改
		array.set(3, 6);
		//删除
		array.remove(4);
		for (int i = 0; i < array.size(); i++) {
			System.out.println(array.get(i));
		}
	}
}

二、集合

1.概述

Java 集合是使程序能够存储和操纵元素 不固定的一组数据。所有 Java 集合类都位于 java.util 包中

既然已经有了数组来存放多个元素,为什么还有提供 Java 集合工具类

  • 我们通过对比数组和 Java 集合工具类来解释 Java 集合工具类的必要性

数组

集合

长度固定

长度不固定

存放任意类型

不能存放基本数据类型,只能存放对象的引用

**注意:**如果集合中存放基本类型,一定要将其"装箱"成对应的"基本类型包装类"

2.继承体系

Java list foreach 方法活的下标 list. foreach_ci

Collection 是集合,两个直接子接口是 List 和 Set

List 特性:有序 可重复,保证数据的添加顺序和取出顺序一致

Set 特性:无序 不可重复,不能保证数据的添加和取出顺序一致

3.Collection

3.1.常用方法

Java list foreach 方法活的下标 list. foreach_java_02

3.2.使用方式

import java.util.ArrayList;
import java.util.Collection;

public class Collection_01 {
	public static void main(String[] args) {
		Collection c=new ArrayList();
		//添加
		c.add("a");
		c.add("b");
		c.add("n");
		//删除,根据内容删除
		c.remove("a");
		//Collection 中没有提供查询和修改
		//已有元素个数
		System.out.println(c.size());
		System.out.println("b"+c.contains("b"));
		//清空
		c.clear();
		//判断是否为空(是否个数为0)
		System.out.println("空"+c.isEmpty());
		c.add("a");
		c.add("b");
		c.add("n");
//		c.add(1);  
//		c.add(66);
//		c.add(false);
//		c.add(new Object());
		//转为数组并返回
		Object[] array=c.toArray();
		//增强for循环 
		// for(数据类型 变量 :集合/数组){循环体}
		for (Object o : array) {
			System.out.println(o);
		}
	}
}

3.3.Iterator

  • Interator 迭代器,屏蔽了不同数据结构的底层结构差异性,提供了遍历的统一方式
  • 迭代器为遍历提供了三个方法
  • boolean hasNext() :判断是否还有元素,有就返回 true
  • next() : 返回当前指向的元素,并让光标指向下一个元素
  • remove() : 删除当前指向的元素,当使用迭代器时,想要删除数据,只能使用迭代器的删除,(一般在外部用其他方法删除后再进行迭代)
  • 只要迭代器生成,那么集合中就不能添加或删除元素,如果需要添加或删除,则需要重新生成迭代器
public class Collection_02 {
	public static void main(String[] args) {
		Collection c=new ArrayList();
		//添加
		c.add("a");
		c.add("b");
		c.add("n");
		//c.remove("n"); 想要删除数据一般在外部删除
		//创建迭代器
		Iterator it=c.iterator();
		while (it.hasNext()) {
			Object object=it.next();
			System.out.println(object);
			//迭代器中的删除,一般不会使用
			if (object.equals("n")) {
				it.remove();
			}
		}
		System.out.println(c);
		//迭代器使用完后,光标不会自动复原,还会停留在上一次移动后的位置
		//想要再次使用 只能重新生成
		it=c.iterator();
		while (it.hasNext()) {
			Object object=it.next();
			System.out.println(object);
		}
	}
}

4.forEach

  • 增强 for 循环,是 Iterator 的简写方式,但是功能不太全
  • 使用增强 for 循环时,也不能对集合进行添加和删除
  • 语法:for( 数据类型 变量 :集合 / 数组){ 循环体 }
Collection c=new ArrayList();
		c.add("a");
		c.add("b");
		c.add("n");
		// for(数据类型 变量 :集合/数组){循环体}
		for (Object o : array) {
             // o 是集合中的每个元素,相当于每次都会给 o 赋值,类似o[i] 
			System.out.println(o);
		}

5.List

  • List 有序可重复
  • 有序:添加和取出顺序一致,队列操作
  • 可重复:可以存储重复数据

5.1.ArrayList

  • ArrayList 底层是数组,查询和修改效率极高,但是随机添加和删除效率较低
  • 默认加载长度是 10 ,扩容后是原来的 1.5 倍, 非线程安全,效率极高
  • 当第一次添加数据的时候,再创建数组,我们如果只是 new 了 ArrayList()的对象时,是并不会初始化底层数组的

java.util 中的源代码:

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
    	//这里就是扩容为原来的1.5倍        oldCapacity / 0.5     
        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);
    }

例:

public class Collection_03_List {
	public static void main(String[] args) {
		List list=new ArrayList();
		//添加到尾部
		list.add("a");
		list.add("b");
		list.add("c");
		list.add(6);
		System.out.println(list);
		//添加到指定位置
		list.add(1, 66);
		System.out.println(list);
		//根据内容删除
		list.remove("a");
		System.out.println(list);
		//根据下标删除
		list.remove(2);
		System.out.println(list);
		//如果我们要根据内容删除,且该内容是数字
		//那么必须将该值转换为对应的包装类,否则会被当做下标
		list.remove(Integer.valueOf(6));// 6 就是想要删的内容,不是下标
		System.out.println(list);
		//根据下标修改
		list.set(1, 666);
		System.out.println(list);
		//根据索引进行查询 ( 索引即下标,下标即索引 )
		System.out.println(list.get(1));
		//个数
		System.out.println(list.size());
		//判断是否包含
		System.out.println(list.contains(666));
		//判断是否为空 ( 个数是否为0 )
		System.out.println(list.isEmpty());
		//清空
		list.clear();
		//获取对应元素的索引下标,如果找不到则返回-1
		System.out.println("下标"+list.indexOf(Integer.valueOf(666)));
		//遍历
		list.add("a");
		list.add("b");
		list.add("c");
		for (int i = 0; i < list.size(); i++) {
			System.out.print(list.get(i)+" ");
		}
		for (Object o : list) {
			System.out.print(o+" ");
		}
	}
}

5.2.LinkedList

5.2.1.个人理解

个人理解:可以把链表看成曲别针,多个曲别针两端互相连接成为一条链,假如只有一个曲别针,那么它的"头部"就是这条链的"头部",同时"尾部"也是这条链的"尾部",如果向头部添加新的曲别针,那么新的曲别针的"尾部"就会连接(指向)旧曲别针的"头部",新曲别针的"头部"就会变成这条链的"头部",同理,如果向尾部部添加新的曲别针,那么新的曲别针的"头部"就会连接(指向)旧曲别针的"尾部",新曲别针的"尾部"就会变成这条链的"尾部"

5.2.2.基本使用
  • LinkedList 底层是双向链表,随机添加和删除效率极高,但是查询和更改效率极低
  • 双向:互相可以找到,可以向下找,也可以向上找
  • 链表中存储的都是节点对象,每一个双向链表中的节点都包含三个属性:保存的值,下一个节点的引用,上一个节点的引用
  • 基本用法和 ArrayList 类似,只不过 LinkedList 多了一些用法 (++)
public class Collection_04_LinkedList {
	public static void main(String[] args) {

		LinkedList list=new LinkedList();
		//添加到尾部
		list.add("a");
		list.add("b");
		list.add("c");
		list.add(6);
		list.addLast(9);             ++
		list.offerLast(99);          ++
		System.out.println(list);
		//添加到头部
		list.push(8);                ++
		list.addFirst(88);           ++
		list.offerFirst(888);        ++
		//添加到指定位置
		list.add(1, 66);
		System.out.println(list);
		//根据内容删除
		list.remove("a");
		System.out.println(list);
		//根据下标删除
		list.remove(2);
		System.out.println(list);
		//如果我们要根据内容删除,且该内容是数字
		//那么必须将该值转换为对应的包装类,否则会被当做下标
		list.remove(Integer.valueOf(6));// 6 就是想要删的内容,不是下标
		System.out.println(list);
		//根据下标修改
		list.set(1, 666);
		System.out.println(list);
		//根据索引进行查询 ( 索引即下标,下标即索引 )
		System.out.println(list.get(1));
		System.out.println("首"+list.getFirst());        ++
		System.out.println("尾(#`O′)"+list.getLast());   ++
		//获取第一个元素,并删除该元素,如果没有该元素(个数为0)则报错java.util.NoSuchElementException
		System.out.println(list.pop());                  ++
		//获取第一个元素,并删除该元素,如果没有该元素(个数为0)则返回null
		System.out.println(list.poll());                 ++
		System.out.println(list);
		//个数
		System.out.println(list.size());
		//判断是否包含
		System.out.println(list.contains(666));
		//判断是否为空 ( 个数是否为0 )
		System.out.println(list.isEmpty());
		//清空
		list.clear();
		//获取对应元素的索引下标,如果找不到则返回-1
		System.out.println("下标"+list.indexOf(Integer.valueOf(666)));
		//遍历
		list.add("a");
		list.add("b");
		list.add("c");
		for (int i = 0; i < list.size(); i++) {
			System.out.print(list.get(i)+" ");
		}
		for (Object o : list) {
			System.out.print(o+" ");
		}
	}
}
5.2.3.底层实现

java.util …

5.2.3.1.节点类Node
private static class Node<E> {//是 LinkedList 中的静态内部类
        E item;//我们要保存的元素 比如 add(6) 这里就保存6
        Node<E> next;//下一个节点的对象地址
        Node<E> prev;//上一个节点的对象地址
		//构造方法
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
5.2.3.2.LinkedList
transient int size = 0;// 已添加元素个数

    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    transient Node<E> first;//首节点

    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    transient Node<E> last;//尾节点
5.2.3.3.添加-add
public boolean add(E e) {
        linkLast(e);
        return true;//添加成功则返回true
    }
private void linkFirst(E e) {//头部添加
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }
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++;
    }

上面代码看不懂就看图,图看不懂可以看看上面我的个人理解,好好想一下

Java list foreach 方法活的下标 list. foreach_数组_03

5.2.3.4.获取-get
public E get(int index) {
        checkElementIndex(index);//校验下标是否合法,不合法则抛出下标越界异常
        return node(index).item;//下标合法则获取数据并返回
    }
//校验函数
private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
    }
//获取数据
Node<E> node(int index) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

链表中是没有下标的,但是提供了下标操作的方法,但是并不是下标,本质上是帮我们进行遍历查找,也是通过 next 一个一个找的

5.2.3.5.删除原理
public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }

E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;  //节点x处的元素
        final Node<E> next = x.next;//节点x的下一个节点
        final Node<E> prev = x.prev;//节点x的上一个节点
		//如果x的上一个节点为空,说明x是首节点
        if (prev == null) {
            first = next;
        } else {
            prev.next = next;
            x.prev = null;
        }
		//如果x的下一个节点为空,说明x是尾节点
        if (next == null) {
            last = prev;
        } else {
            next.prev = prev;
            x.next = null;
        }

        x.item = null;//把节点保存的元素删除
        size--;  //个数
        modCount++;
        return element;
    }