JAVA开发学习-day06

JAVA开发学习-day06

集合是容器的一种,之前学过的数组也是一种容器,但数组有弊端,数组只能存储指定的数据类型,且数组的长度不能改变,所有我们在存储数据时可以使用集合,常用的集合有List,Set,Map

1. 列表 List

List接口继承自Collection接口,并添加了一些针对有序列表的操作。它允许元素的重复,并提供了根据索引访问、添加、删除和替换元素的方法。在Java中,List接口有几个常见的实现类,每个实现类都具有不同的性能和用途。

  • ArrayList:基于动态数组实现,支持随机访问和快速遍历,适用于读取和修改操作较多的场景。
  • LinkedList:基于双向链表实现,支持高效的插入和删除操作,适用于频繁的插入和删除操作。
  • Vector:与ArrayList类似,但是线程安全,适用于多线程环境。

1.1 ArrayList

ArrayList是List接口的一个常见实现类,它基于动态数组实现,可以根据需要自动扩展和收缩数组的大小。

ArrayList中的数组是Object类数组,所以可以存储任意类型的数据

下面是一些ArrayList类中常用的方法

add(E element): 在列表的末尾添加元素。

List list = new ArrayList();

//List中常用方法
//添加数据  List可以存储任意类型 List中存储数据的的是Object数组
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);

System.out.println(list); //[A, 12, null, 33.33]
list.add(1,"44"); // 在下标为1的位置上位置上,该下标的元素以及后面的元素依次向后
System.out.println(list); // [A, 44, 12, null, 33.33]
//插入的位置之前必须都有元素
list.add(5,989);
System.out.println(list); // [A, 44, 12, null, 33.33, 989]
//list.add(10,123)  会造成下标越界

get(int index): 获取指定索引位置的元素。

List list = new ArrayList();

//List中常用方法
//添加数据  List可以存储任意类型 List中存储数据的的是Object数组
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]

//获取元素
Object obj = list.get(2); //传入下标
System.out.println(obj); // null

set(int index, E element):设置指定索引位置的元素

public static void main(String[] args) {
    List list = new ArrayList();
    list.add("A");
    list.add(12);
    list.add(null);
    list.add(33.33);
    System.out.println(list); //[A, 12, null, 33.33]
    //设置元素
    list.set(2,22);
    System.out.println(list); // [A, 12, 22, 33.33]
}

boolean contains(Object o):判断元素是否存在列表中

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]
//列表中是否包含元素判断
boolean bool = list.contains(12);
System.out.println(bool); //true
bool = (list.indexOf(12) != -1);
System.out.println(bool); //true

boolean containsAll(Collection<?> c):判断该集合是否包含传入的集合

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]

//列表中是否包含元素判断
List listA = new ArrayList();
listA.add(33.33);
listA.add(null);

// list ⊇ listA
System.out.println(listA); // [33.33, null]
Boolean bool = list.containsAll(listA); // list是否包含listA
System.out.println(bool); //true

boolean addAll(Collection<? extends E> c):将传入的集合中的所有元素添加到集合中

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]
//列表中是否包含元素判断

List listA = new ArrayList();
listA.add(33.33);
listA.add(null);

list.addAll(listA);
System.out.println(list); // [A, 12, null, 33.33, 33.33, null]

boolean remove():删除传入的列表元素或删除传入索引指向的元素

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]
//删除元素
list.add("A");
list.add("A");
//删除找到的第一个元素
System.out.println(list.remove("A")); //true
System.out.println(list);// [12, null, 33.33, A, A]
//删除下标所在的元素
//参数传入对象 删除这个对象 返回 boolean
//参数传下标 删除下标位置对象 返回删除对象
//传入数字只能用于删除下标
Object obj = list.remove(2);
System.out.println(list); // [12, null, A, A]
System.out.println(obj); //33.33
//想要删除基本数据类型需要先装箱
Boolean bool = list.remove((Integer)12);
System.out.println(list); //[null, A, A]
System.out.println(bool); //true

Iterator iterator():返回一个迭代器对象

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
System.out.println(list); //[A, 12, null, 33.33]
//遍历元素
list.add("A");
list.add("A");
for(int i = 0; i < list.size(); i++){
    System.out.print(list.get(i) + " ");
}
System.out.println();
for(Object item : list){
    System.out.print(item + " ");
}
System.out.println();
Iterator it = list.iterator(); //获取了一个迭代器
while(it.hasNext()){
    System.out.println(it.next());
}

在Java中,for-each循环(也称为增强型for循环)不支持在迭代过程中直接修改底层集合的元素(除非是通过集合元素的内部可变状态),因为它不提供对元素索引的访问。

List listA = new ArrayList();
listA.add("A");
listA.add(12);
listA.add(null);
listA.add(33.33);

List list = new ArrayList();
list.add("A");
list.add(12);
list.add(null);
list.add(33.33);
list.add(listA);
System.out.println(list); //[A, 12, null, 33.33, [A, 12, null, 33.33]]

//在Java中,for-each循环(也称为增强型for循环)不支持在迭代过程中直接修改底层集合的元素(除非是通过集合元素的内部可变状态),因为它不提供对元素索引的访问。
for(Object t : list) {
    //类型判断
    if (t instanceof Double) {
        if ((Double) t == 33.33) {
            //此时t的指向由list中的33.33变为指向12.222,所以列表中的33.33并没有改变
            t = 12.222;
        }
    }
    if(t instanceof List){
        List objs = (List)t;
        //t指向的是列表中的列表,改变了它指向的列表中的值,但它的指向的列表的内存地址没变
        objs.set(1,"abc");
    }
}
System.out.println(list); //[A, 12, null, 33.33, [A, abc, null, 33.33]]

来看一下ArrayList的扩容方式

先看ArrayList中的add方法

public boolean add(E e) {
    //与AbstractStringBuilder中的方法名一样,作用也是类似的,确保容量够用
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

再看ensureCapacityInternal方法,先通过calculateCapacity得出需要的容量为多大,再通过ensureExplicitCapacity来判断是否需要扩容

private void ensureCapacityInternal(int minCapacity) {
    //通过calculateCapacity方法确定需要的容量是多少,再通过ensureExplicitCapacity来判断是否需要扩容
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

初始容量大小为空,如果需要的容量小于等于ArrayList中给出的默认容量(DEFAULT_CAPACITY),则容量大小就设置为默认容量(大小为10),如果大于默认容量则设置为需要的容量(size + 1),如此就确认了需要的容量大小

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //DEFAULT_CAPACITY值为10,最少给10的容量
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

来看ensureCapacityInternal中的ensureExplicitCapacity,来判断是否扩容,如果需要的容量大于当前数组的长度,则使用grow方法来扩容

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // 如果需要的容量大于当前数组的长度,则进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

在grow方法中先将容量扩容至原来的1.5倍,如果还是小于需要的容量则将容量设置为需要的容量,再与能够设置的最大容量值比较,如果大于最大容量再使用hugeCapacity()扩容

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //扩容至原容量的1.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:
    // 使用Arrays.copyOf扩容存储数据的数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

hugeCapacity方法

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // 为负数则抛出异常
        throw new OutOfMemoryError();
    //Integer.MAX_VALUE为2^31-1, MAX_ARRAY_SIZE为2^31-9,多余的8是安全内存
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

ArrayList的扩容方式总的来说就是,默认初始容量 10
扩容时候,先扩容1.5倍(原容量 + 原容量向右移动一位),如果不够设置为需要的容量

2. 代码块

代码块也叫初始化块 属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{ }连接起来。

简而言之,就是代码被放在一对大括号中

2.1 静态代码块

public class HomeWork {
    //静态代码块
    //static修饰,随着类加载而加载,只执行一次
    //用于类加载时做一些初始化操作
    static {
        System.out.println("这是一个静态代码块");
    }

    public static void main(String[] args) {
        //调用HomeWork类中的主函数方法时先加载
        //第一次调用TestA时候第一次加载TestA类,执行一次静态代码块
        new TestA();
        //第二次就不会再执行静态代码块
        new TestA();
        //这是一个静态代码块
        //这也是一个静态代码块
    }
}
class TestA{
    static {
        System.out.println("这也是一个静态代码块");
    }
}

2.2 成员代码块(实例代码块)

public class HomeWork {

    public static void main(String[] args) {
        new TestA();
        new TestA();
        //这也是一个静态代码块
        //这是一个实例代码块
        //这是一个实例代码块
        
        
        //静态代码块在实例代码块前执行
    }
}

class TestA{
    //实例代码块
    {
        System.out.println("这是一个实例代码块");
    }
    static {
        System.out.println("这也是一个静态代码块");
    }
}

2.3 代码块和构造方法的执行顺序

块的执行顺序
1.父类静态代码块
2.子类静态代码块
3.父类的成员代码块
4.父类的构造构造方法
5.子类的成员代码块
6.子类的构造方法

public class EasyBlock {
    static {
        //静态代码块  一个类的静态代码块只会执行一次
        //加载类对象时执行
        System.out.println("这是一个静态代码块");
    }
    {
        //成员代码块
        System.out.println("这是成员代码块");
    }
    EasyBlock(){
        System.out.println("构造方法");
    }
    public static void main(String[] args) {
        //每次new都会执行成员代码块,成员代码块在构造方法前执行
        new EasyChild();
        
        //这是一个静态代码块
        //这是子类的静态代码块
        //这是成员代码块
        //构造方法
        //这是子类的成员代码块
        //子类的构造方法
    }
}
class EasyChild extends EasyBlock {
    static {
        //静态代码块
        System.out.println("这是子类的静态代码块");
    }

    //成员代码块
    {
        System.out.println("这是子类的成员代码块");
    }

    EasyChild(){
        System.out.println("子类的构造方法");
    }
}

3. 内部类

汽车的发动机和汽车都可以看做是类,但是汽车的发动机类单独存在没什么意义,所以我们可以直接将其定义为内部类

class Car{
    String carName;
    int carAge;
    String carColor;
    class Engine{
        String engineName;
        int engineAge;
    }
}

3.1 成员内部类

/*
 * 成员内部类,属于外部类的成员
 * 获取成员内部类的两种方式
 * 方式一:
 *    当成员内部类被private修饰时,外部类编写方法,对外提供内部类对象
 * 方式二:
 *    当成员内部类没有被private修饰时,直接创建对象
 * 格式: outer.inner 对象名 = new outer(). new inner();
 * */

方式一:

public class HomeWork {

    public static void main(String[] args) {
        Car car = new Car();
        car.getEngine();
    }
}
class Car{
    String carName;
    int carAge;
    String carColor;
    public Engine getEngine(){
        return new Engine();
    }
    private class Engine{
        String engineName;
        int engineAge;
    }
}

方式二:

public class HomeWork {

    public static void main(String[] args) {
        Car.Engine engine = new Car().new Engine();
    }
}
class Car{
    String carName;
    int carAge;
    String carColor;
    class Engine{
        String engineName;
        int engineAge;
    }
}

3.2 静态内部类

由static修饰的内部类,可以使用类名直接调用

public class HomeWork {

    public static void main(String[] args) {
        Car.Engine engine = new Car.Engine();
        engine.engineName = "aa";
        System.out.println(engine.engineName); //aa
    }
}

class Car{
    String carName;
    int carAge;
    String carColor;

    //静态内部类
    static class Engine{
        String engineName;
        int engineAge;
    }
}

3.3 匿名内部类

在使用抽象类或接口的抽象方法时,我们可能只实现一个只在这里使用的方法,不想再写一个实体类,这个时候就可以使用匿名内部类来解决这个问题

class Car{
    Interface1 interface1 = new Interface1() {
        @Override
        public void method() {
            System.out.println("实现接口抽象方法");
        }
    };
    Abstract anAbstract = new Abstract() {
        @Override
        public void method() {
            System.out.println("实现抽象类的抽象方法");
        }
    };

    String carName;
    int carAge;
    String carColor;

    //静态内部类
    static class Engine{
        String engineName;
        int engineAge;
    }
}

@FunctionalInterface
interface Interface1{
    void method();
}

abstract class Abstract{
    public abstract void method();
}

lambda表达式

lambda表达式分为三部分,方法参数、lambda操作符和函数式接口方法的实现逻辑。

()里面参数的个数,根据函数式接口里面抽象方法的参数个数来决定。

当只有一个参数的时候,()可以省略

当expr逻辑非常简单的时候,{}和return可以省略

在这里插入图片描述

public class HomeWork{
    public static void main(String[] args) {
        TestClass testClass = new TestClass();
        testClass.interface1.method();
        int result = testClass.interface2.method(3,4);
        Interface1 i1 = TestClass::fun1;
        Interface2 i2 = TestClass::fun2;
        i1.method();
        i2.method(5,5);
    }
}


class TestClass{
    Interface1 interface1 = () -> {
        System.out.println();
    };
    Interface2 interface2 = (int a, int b) -> {
        return a + b;
    };
    public static void fun1(){

    }
    public static int fun2(int a,int b){
        return 1;
    }

    int result = interface2.method(3,4);
}

@FunctionalInterface
interface Interface1{
    void method();
}

interface Interface2{
    int method(int a,int b);
}

abstract class Abstract{
    public abstract void method();
}

3.4 局部内部类

定义在方法中的类,只在方法中使用

//局部内部类
public void inner(){
    //只在方法中使用
    class InnerC{
    }
    new InnerC();
}

() -> {
System.out.println();
};
Interface2 interface2 = (int a, int b) -> {
return a + b;
};
public static void fun1(){

}
public static int fun2(int a,int b){
    return 1;
}

int result = interface2.method(3,4);

}

@FunctionalInterface
interface Interface1{
void method();
}

interface Interface2{
int method(int a,int b);
}

abstract class Abstract{
public abstract void method();
}


### 3.4 局部内部类

定义在方法中的类,只在方法中使用

```java
//局部内部类
public void inner(){
    //只在方法中使用
    class InnerC{
    }
    new InnerC();
}
  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值