Java Day14

Java

day14——2023.8.24

iterator()方法

iterator()方法,来自于Collection接口 的 父接口 Iterable 接口
Iterable接口其实就定义了Iterator iterator();这个抽象方法,其实就是用来指定将来接口中迭代元素的规则
ArrayList 实现了List接口,List接口,继承自Collection接口,Collection接口中,存在iterator()方法
ArrayList 类中,必定重写了 iterator()方法,因为iterator()是父接口的抽象方法
ArrayList中的方法如下 :
在这里插入图片描述
返回值是 new Itr(), 说明Itr是 Iterator接口的自实现类,那么既然Itr是实现类,必定会重写Iterator接口中的抽象方法 hasNext() ,next()

通过查看源码,发现 Itr类,是ArrayList中的一个内部类
在这里插入图片描述
在这里插入图片描述
分析这个内部类的源码:
有一个属性 cursor ,用来记录每次返回的下一个元素的索引
重写的hasNext()方法,用来判断 cursor是否返回到最后了,和list的size相同,就结束
在这里插入图片描述
重写的next()方法,用来将指定的元素返回,每次返回后,将cursor指向下一个元素

public E next() {
    checkForComodification();
    int i = cursor;
    
    Object[] elementData = ArrayList.this.elementData;
 
    cursor = i + 1;
    return (E) elementData[lastRet = i];
}

ArrayList总结

1,ArrayList底层是动态扩容的数组,使用无参构造创建ArrayList的时候,其实创建的是一个空数组,当放入第一个元素开始,扩容为默认容量10

2,当原来的容量放满之后,放入下一个元素的时候,数组开始扩容,扩容到原来容量的1.5倍,每次扩容都会进行底层的数组复制的操作

3,频繁的扩容,会降低ArrayList的效率 ,所以,如果知道要存入多少个元素,那么在声明的时候就直接指定容量,可以加大ArrayList的效率

4,ArrayList 是有序的,指的是存取的顺序有序,可以存放 null值,可以存放重复的元素

5,ArrayList是线程不安全的

Vector和ArrayList的区别

Vector是JDK1.2的类,是一个比较老旧的集合

Vector的底层也是数组,和ArrayList的区别是 :

Vector是线程同步的(安全的),它类中的方法上都加了synchronized来修饰,所以它的效率低一点

Vector的扩容,每次扩容2倍

LinkedList的使用

LinkedList底层是双向链表

既然是双向链表,优点是往集合中插入和删除元素较快,查找指定元素效率低一些

LinkedList还实现了Deque接口,所以LinkedList中,也有操作队列、栈的方法
在这里插入图片描述
LinkedList既然是链表的底层,那么必然存在Node 节点 ,在这里声明了一个内部类Node用来表示节点:
在这里插入图片描述

LinkedList常用的方法
public class LinkedListDemo {
    public static void main(String[] args) {
        //创建LinkedList对象,泛型是String
       /* LinkedList<String> linkedList = new LinkedList<>();

        //调用普通的list中的方法
        linkedList.add("hello");
        linkedList.add("world");
        linkedList.add("java");
        linkedList.add("mysql");

        linkedList.add(4,"spring");
        System.out.println(linkedList);

        String s = linkedList.get(3);
        System.out.println(s);

        linkedList.remove(1);
        System.out.println(linkedList);

        //遍历
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }
        System.out.println("--------------");

        for (String s1 : linkedList) {
            System.out.println(s1);
        }
        System.out.println("--------------");

        Iterator<String> iterator = linkedList.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        */

        //栈结构相关的方法
        //E pop()
        //从此列表表示的堆栈中弹出一个元素。
        //void push(E e)
        //将元素推送到由此列表表示的堆栈上。
       /* LinkedList<String> list1 = new LinkedList<>();

        list1.push("hello");
        list1.push("world");
        list1.push("java");
        list1.push("mysql");

        System.out.println(list1);
        String pop = list1.pop();
        System.out.println(pop); //mysql
        System.out.println(list1);

        */

        //队列结构相关方法
        //void addFirst(E e)
        //在该列表开头插入指定的元素。
        //void addLast(E e)
        //将指定的元素追加到此列表的末尾。
        //E getFirst()
        //返回此列表中的第一个元素。
        //E getLast()
        //返回此列表中的最后一个元素。
        //E pollFirst()
        //检索并删除此列表的第一个元素,如果此列表为空,则返回 null 。
        //E pollLast()
        //检索并删除此列表的最后一个元素,如果此列表为空,则返回 null 。
        //E removeFirst()
        //从此列表中删除并返回第一个元素。
        //E removeLast()
        //从此列表中删除并返回最后一个元素。

        LinkedList<String> linkedList1 = new LinkedList<>();
        linkedList1.addFirst("1");
        linkedList1.addFirst("2");
        linkedList1.addFirst("3");
        linkedList1.addLast("4");
        linkedList1.addLast("5");
        linkedList1.addLast("6");
        System.out.println(linkedList1);

        System.out.println(linkedList1.getFirst()); //3
        System.out.println(linkedList1.getLast());//6
        System.out.println(linkedList1);

        String s1 = linkedList1.pollFirst();
        System.out.println(s1);
        String s2 = linkedList1.pollLast();
        System.out.println(s2);
        System.out.println(linkedList1);

        linkedList1.removeFirst();
        linkedList1.removeLast();
        System.out.println(linkedList1);

    }
}
LinkedList源码解析

public class LinkedList

​ extends AbstractSequentialList

​ implements List, Deque, Cloneable, java.io.Serializable

{

​ //记录当前双向链表的长度

​ transient int size = 0;

​ //记录当前双向链表的头节点

​ transient Node first;

​ //记录当前双向链表的尾节点

​ transient Node last;

构造方法

LinkedList的构造方法有两个:

public LinkedList() {

}

public LinkedList(Collection<? extends E> c) {

​ this();

​ addAll©;

}

普通方法

add方法

add方法实际上就是往链表最后添加元素

​ public boolean add(E e) {

​ linkLast(e);

​ return true;

​ }

linkLast(E)方法可以在当前双向链表的尾节点之后添加一个新的节点,并且调整last属性的指向位置

void linkLast(E e) {
    //使用一个临时变量来记录操作前的last属性信息
    final Node<E> l = last;
    //创建一个新的节点,item属性值为e,新节点的前置对象指向原来的尾节点,后置节点为null
    final Node<E> newNode = new Node<>(l, e, null);
    //因为要在双向链表的尾节点添加新的节点,将last属性中的信息重新设置
    last = newNode;
    //条件成立,说明双向链表没有任何节点
    if (l == null)
        //将first节点指向新的节点,first和last都同时指向同一个节点
        first = newNode;
    else
        //不成立,双向链表至少有一个节点,将原来的尾节点的后置节点指向新的尾节点
        l.next = newNode;
    //双向链表长度 + 1 
    size++;
    //linkedList集合的操作次数 + 1
    modCount++;
}
addLast方法,同add方法

public void addLast(E e) {

​ linkLast(e);

}

addFirst方法

public void addFirst(E e) {

​ linkFirst(e);

}

/**

* Links e as first element.

*/

private void linkFirst(E e) {
    //使用一个临时的变量记录操作前first属性的信息
    final Node<E> f = first;
    //创建一个数据信息为e的新节点,该节点前置节点引用为null,后置节点引用指向原先的头节点
    final Node<E> newNode = new Node<>(null, e, f);
    //因为要在双向链表头部添加新的节点,将first属性中的信息重新设置
    first = newNode;
    //条件成立,说明双向链表没有任何节点
    if (f == null)
        //将last节点也指向新的节点,这样first和last节点属性同时指向同一个节点
        last = newNode;
    else
        //不成立,说明双向链表至少有一个节点,只需要把原来的头节点的前置节点引用指向新的头节点
        f.prev = newNode;
    //双向链表长度 + 1
    size++;
    //linkedList集合的操作次数 + 1
    modCount++;
}
remove方法
 public boolean remove(Object o) {
    if (o == null) {
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);
                return true;
            }
        }
    } else {
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);
                return true;
            }
        }
    }
    return false;
}
实际调用的是unlink()方法
E unlink(Node<E> x) {
    // assert x != null;
    //定义一个element变量,记录当前节点中的数据对象,以便方法最后返回
    final E element = x.item;
    //创建一个next节点,记录当前节点中的后置节点引用,可能为null
    final Node<E> next = x.next;
    //创建一个prev节点,记录当前节点中的前置节点引用,可能为null
    final Node<E> prev = x.prev;

    //如果条件成立,说明被移除的x节点是双向链表的头节点
    if (prev == null) {
        //将x的后置节点设置为新的头节点
        first = next;
    } else {
        //将x的前置节点中的后置节点设置为移除的x节点的后置节点
        prev.next = next;
        //将移除的x节点的前置节点设置为null
        x.prev = null;
    }

    //如果条件成立,说明被移除的x节点是双向链表的尾节点
    if (next == null) {
        //将移除的x的节点的前置节点设置为新的尾节点
        last = prev;
    } else {
        //将x的后置节点中的前置节点设置为移除x节点的前置节点
        next.prev = prev;
        //将移除的x节点的后置节点设置为null
        x.next = null;
    }

    //将移除的x节点中的数据对象设置为null
    x.item = null;
    //双向链表长度 - 1
    size--;
    //LinkedList集合操作次数 + 1
    modCount++;
    return element;
}
get方法

可以看到get方法实现就两段代码:

​ public E get(int index) {

​ checkElementIndex(index);

​ return node(index).item;

​ }

我们进去看一下具体的实现是怎么样的:

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;
        }
    }
set方法

set方法和get方法其实差不多,根据下标来判断是从头遍历还是从尾遍历


    public E set(int index, E element) {
        checkElementIndex(index);
        Node<E> x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }
List总结

常用的子类ArrayList,LinkedList,Vector,有序(存取有序),可以存放null,可以重复

ArrayList:

底层是动态扩容数组、初始容量默认是0,放入元素变为10,存放第11个元素开始扩容,扩容到原来的1.5倍,查询效率快,增删效率较慢,因为增删的时候,需要数组的拷贝(native方法,c/c++实现)

LinkedList:

底层是双向链表,查询效率较低,从头到尾遍历,增删效率较高

Vector :

底层也是数组,初始容量默认为10,每次扩容2倍,方法都是同步的,效率相对较慢

一般将来,查询多用ArrayList,增删多用LinkedList

其实增加元素的时候,如果一直是增加到末尾的话,使用ArrayList也很快,一直删除末尾元素,ArrayList也快

1,什么是自动装箱和自动拆箱

自动装箱:将基本类型的值,直接赋值给对应的包装类对象。
自动拆箱:将包装类型的对象,直接赋值给对应基本类型变量

2,常见的5个运行时异常

NullPointerException(空指针异常)
ArrayIndexOutOfBoundsException(数组越界异常)
ClassCastException(类转换异常)
IllegalArgumentException(非法参数异常)
ArithmeticException(算术异常)

3,throw和throws区别

throw声明在方法内
throws声明在方法体中
throw抛出一个具体的异常类型
throws声明一个方法可能抛出的所有异常信息,表示将来异常的一种可能性,但并不一定发生异常
throw需要用户自己捕获相关异常,然后再对其处理,最后将异常信息抛出
throws通常不需要显式的捕获异常,由系统自动将异常抛给上级方法

4,final、finally、finalize 三个的区别

final 是一个修饰符,用于修饰类、方法和变量,分别表示不可继承、不可重写和不可修改。
finally 是一个关键字,用于定义在异常处理中一定会被执行的代码块。finalize 是 Object 类的一个方法,用于对象的垃圾回收前的清理操作,

5,String和StringBuffer、StringBuilder的区别

String声明的字符不可变,可以看做是字符串常量,String的内容一旦改变,引用就改变了,可以看成是一个新的引用,所以,String类型做字符串拼接,比较耗时
StringBuffer称为字符串缓冲区,提供了一些方法,可以改变该缓冲区中的数据,但是对应的引用没有改变,相对来说StringBuffer效率更快
StringBuffer和StringBuilder ,他们都是字符串缓冲区,只不过StringBuffer的方法多了一个 synchronized关键字,它是线程安全的。多线程情况下,用StringBuffer,单线程就用StringBuilder

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值