Bootstrap

Java 常用类大讲解!3️⃣(手写 API、源码必备)

五、数组高级

5.1、冒泡排序法

​ 对未排序的各元素从头到尾依次比较相邻两个元素的大小关系,如果前一个元素大于后一个元素则交换位置,经过第一轮比较后可以得到最大值,同理第二轮比较后出现第二大值等

第1轮比较:需要比较5次,比较完出现最大值。
第2轮比较:需要比较4次,比较完出现第二大值。
第3轮比较:需要比较3次,比较完出现第三大值。
...
可以看出如有N个元素,则需要N-1轮比较,第M轮需要N-M次比较。
i(轮数)              次数           每轮次数的规律
  0                   3            数组长度-i-1
  1                   2            数组长度-i-1
  2                   1            数组长度-i-1

package ArraysAdvanced;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/14 14:04
 */
public class Bubble {
  public static void main(String[] args) {
    int[] nums = new int[10];//创建一个数组
    for (int i = 0; i < nums.length; i++) {
     nums[i] = new Random().nextInt(100);//对数组进行随机赋值,
    }
    System.out.println(Arrays.toString(nums));//打印原始的数组(未排序之前的)
    for (int i = 0 ; i < nums.length-1 ; i++){//定义一个控制循环控制总共需要冒泡几轮:数组长度-1
      for (int j = 0 ; j < nums.length-1-i ; j++){//控制每轮比较几次
        int temp = 0;//设置临时变量,用于进行两两交换
        if (nums[j]>nums[j+1]){//如果前面的值大于后面的值,那么就进行交换,保证后面的值大于前面的
          temp = nums[j];//将大的值存储在临时变量中
          nums[j] = nums[j+1];//将小的值赋值给大的值
          nums[j+1] = temp;//将临时变量的值赋值给小的值,从而完成了两两交换
        }
      }
    }
    System.out.println(Arrays.toString(nums));//输出排序后的数组
  }
}

5.2、选择排序

​ 从当前位置开始找出后面的较小值与该位置交换

实现思路:
  (1)、控制选择几轮:数组长度-1
  (2)、控制每轮从当前位置开始比较几次
i(轮数)              次数           每轮比较几次
  0                  3             数组长度-i-1
  1                  2             数组长度-i-1
  2                  1             数组长度-i-1

package com.test;
import java.util.Arrays;
import java.util.Random;

/**
 * @author Xiao_Lin
 * @date 2020/12/25 11:31
 */
public class SelectSort {
  public static void main(String[] args) {
   //定义一个数组
        int[] arr = {7, 6, 5, 4, 3};
        System.out.println("排序前:" + Arrays.toString(arr));
      // 定义一个控制循环几轮
        for (int x = 0; x < arr.length; x++) {
            // 定义一个循环控制每轮比较几次,一定是以当前位置与后面的元素进行比较,遍历后面的元素
            // i=0  j=1 2 3
            // i=1  j=2 3
            // i=2  j=3
            for (int i = x+1; i < arr.length; i++) {
                //拿当前的位置与指定的元素进行大小比较,后面的较小就交换位置
                if (arr[x] > arr[i]) {
                    int temp = arr[x];
                    arr[x] = arr[i];
                    arr[i] = temp;
                }
            }
        }
        System.out.println("排序后:" + Arrays.toString(arr));
  }
}

5.3、二分查找法

查找数组元素的语法:

  • 线性查找:从头到尾查找,性能很低

  • 二分查找法(折半查找):前提是数组元素必须有序,性能比较好

package day012_ArraysAdvanced.classing;

/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/14 18:39
 */
public class Find {

  /**
   * 二分查找的方法
   * @param nums 需要查找的元素所在的数组
   * @param value 需要查找的元素
   * @return
   */
  public static int binarySearch(int[] nums , int value){//定义一个二分查找的方法
    int low = 0;//定义最低的位置,默认值为0
    int hight = nums.length-1;//定义最高的位置,默认值为数组的最后一位
    int mid , midValue; //定义中间的变量值和序号
    while (low <= hight){//如果最低位小于等于最高位,说明我们查找的元素还在数组织中,否则说明数组中没有该元素,返回-1
      mid = (low+hight)/2;//计算出中间位
      midValue = nums[mid];//取出中间位置的值
      if (value > midValue){//如果需要寻找的变量在中间值的右边
        low = mid+1;//将最低位移到中间位置后一位
      }else if(value < midValue){//如果需要寻找的变量在中间值的左边
        hight = mid-1;//将最高位移到中间位置的前一位
      }else if(value == midValue){//如果中间的值和需要寻找的值相等,说明找到
        return mid;//返回找到的序号
      }
    }
    return -1;//返回-1说明找不到
  }
  public static void main(String[] args) {
    int[] nums = {1,2,3,4,5,6};
    System.out.println(binarySearch(nums, 30));

  }
}

5.3、操作数组的API-Arrays

5.3.1、打印数组元素

public class Test01ToString {
  public static void main(String[] args) {
    int[] arr = new int[] { 10, 20, 30, 40, 50, 60, 70 };
    String str = Arrays.toString(arr);
    System.out.println(str);
 }
}

5.3.2、拷贝数组元素

​ Arrays 中提供了数组复制的方法,copyOf(int[] original, int newLength) 复制指定的数组,截取或者用0填充,他是直接创建一个新的数组,如果我们指定的数组元素长度不够就素截取,如果长度多余就是用0填充System类中提供了数组元素拷贝的方法,并且支持任意类型的数组拷贝,而不仅仅是int类型数组。

package day012_ArraysAdvanced.classing;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/14 16:11
 */
public class ArraysTestDemo {

  public static void main(String[] args) {

    //Arrays的copyOf方法
    int[] nums = {21,3,4,652,2};
    int[] ints = Arrays.copyOf(nums, 10);//我们指定复制后的数组长度为10,说明有多,就是用0填充
    System.out.println(Arrays.toString(ints));
    int[] copyOf = Arrays.copyOf(nums, 3);//我们指定复制后的数组长度为3,长度比原来的数组小,直接从前往后截取
    System.out.println(Arrays.toString(copyOf));

    //System的
    int[] num = {1,3,5,7,9};
    int[] newnum = new int[10];
    //参数解释:需要复制的源数组 从源数组的什么位置开始复制 复制到哪个数组中去 从复制到的数组的哪个位置开始填充值 在原数组中填充的长度是多少
    System.arraycopy(num,0,newnum,0,num.length);
    System.out.println(Arrays.toString(newnum));
  }

5.3.3、数组元素的排序

​ Arrays类中已经提供了数组排序的方法sort,并且是调优之后的,性能非常优异,在开发中只需要我们直接调用该方法即可即可,sotr默认为升序,等到以后我们才可以指定sort的排序方式(降序还是升序)

import java.util.Arrays;
public class Test03Sort{
  public static void main(String[] args) {
    int[] arr = new int[] { 2, 9, 6, 7, 4, 1 };
    System.out.println(Arrays.toString(arr));//排序前
    Arrays.sort(arr);
    System.out.println(Arrays.toString(arr));//排序后
      }
  }
}

5.3.4、数组的二分查找

import java.util.Arrays;

public class Test04Search{
  public static void main(String[] args) {
    int[] arr = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int index = Arrays.binarySearch(arr, 8);
    System.out.println(index);
  }
}

5.4、数组的增删改查操作

5.4.1、数组初始化操作

package day012_ArraysAdvanced.classing.ArraysCRUD;

/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/14 19:17
 */
public class ArraysUtils {
  private Integer[] nums = null;  //声明一个数组
  private int size;//声明数组中元素的个数,不是数组长度

  public  ArraysUtils(int capacity){//构造方法,用于初始化
    if (capacity < 0){//如果传进来的容量值<0,说明这个容量值是不合法的
      System.out.println("数组的容量不可以小于0");
      return;
    }else {
      this.nums = new Integer[capacity];//将传进来的容量值进行初始化一个新的数组
    }
  }

  public   ArraysUtils(){//无参构造器
    this(10);//调用有参构造器,并且传入一个初始化的值
  }

  public Integer[] getNums() {
    return nums;
  }

  public void setNums(Integer[] nums) {
    this.nums = nums;
  }

  public int getSize() {
    return size;
  }

  public void setSize(int size) {
    this.size = size;
  }
}

测试类:

package day012_ArraysAdvanced.classing.ArraysCRUD;

/**
 * @author Xiao_Lin
 * @version 1.0
 * @date 2020/12/14 19:24
 */
public class TestArraysUtils {

  public static void main(String[] args) {
    ArraysUtils arraysUtils = new ArraysUtils(13);
    System.out.println(arraysUtils.getNums().length);
  }
}

5.4.2、数组的修改

 public ArraysUtils set(int num,int index){//定义一个进行修改指定位置元素的方法,返回值为ArraysUtils方便写链式调用
   if (index < 0){
     System.out.println("索引不能小于0");
     return null;
   }if (index >= size){
     System.out.println("索引越界");
     return null;
   }
    nums[index] = num;//将需要修改的值赋给指定元素下表原来的值
    return this;//返回当前对象,方便可以进行链式调用
 }

5.4.3、数组的指定索引的查找

 public Integer get(int index){//定义一个根据指定序列查询的方法
    if (index < 0){
      System.out.println("索引不能小于0");
      return null;
    }if (index >= size){
     System.out.println("索引越界");
     return null;
   }
    return this.nums[index];//返回指定序列的值
 }

5.4.4、数组的打印

public String toString(){//定义一个打印方法
   StringBuilder sb = new StringBuilder();//定义一个 StringBuilder 对象
    if (nums == null){//如果当前数组为空
      return null;//直接返回null
    }if (size == 0){//如果当前数组的长度为0
      return "[]";//返回一个空的字符数组
   }else {
      sb.append("[");//先在StringBuilder后追加一个"["
     for (int i = 0; i < size; i++) {//遍历nums数组,切记这里的i要小于数组的元素个数,而不是长度
       if (i == size-1){//如果需要遍历的元素是数组的最后一个元素
         sb.append(nums[i]).append("]");//在最后面追加一个"]"
       }else {
         sb.append(nums[i]).append(",");//否则就只是追加元素和,
       }
     }
   }
    return sb.toString();
 }

5.4.5、数组的追加

 public ArraysUtils append(int num){
  this.nums[size] = num;//将传进来的值追加到数组的后一位
  size++;//数组的元素加一个
  return this;//返回当前对象,方便链式调用
 }

5.4.5、数组的扩容

​ 因为数组的长度是固定的,此时的nums数组只能存储初始化指定数量的元素,如果再多存储一个就报错:数组索引越界。此时就要考虑在保存操作时对数组做扩容操作。扩容的原理是:

 public ArraysUtils append(int num){
  if (size == nums.length){//如果数组中的元素个数等于数组的长度,说明这个时候需要扩容
    this.nums = Arrays.copyOf(nums,nums.length*2+2);//将copyOf产生的新数组赋值给原来的数组,并且将长度扩大到原来的2倍+2个元素
  }
  this.nums[size] = num;
  size++;
  return this;
 }

5.4.6、数组的删除

 public ArraysUtils delete(int index){//定义删除的方法
   if (index < 0){//如果下标小于0
     System.out.println("索引不能小于0");
     return null;//返回空
   }if (index >= size){//如果下标的值比数组中的元素还大的时候
     System.out.println("索引越界");//返回数组越界
     return null;
   }
   for (int i = index ; i < size-1 ;i++){//遍历数组中的元素
     nums[i] = nums[i+1];//将需要删除的索引的后面的值赋值给前面
   }
   nums[size-1] = null;//将数组的最后一位置空
   size--;//数组的元素个数减一
   return this;//返回当前对象
 }

六、泛型

6.1、泛型的概述

​ 泛型其实就是一种数据类型,主要用于某个类或者接口中的数据类型不确定的时候,可以用一个标识符或者占位符来标书未知的数据类型,然后再使用该类或者该接口的时候可以指定该位置类型的真实类型

​ 泛型可以用到接口、类、方法中,将数据类型作为参数传递,其实更像是一种数据类型的模板。

​ 如果不使用泛型的话,从容器中获取出来的元素,由于我们不知道类型,所以需要强制类型转换

6.2、自定义和使用泛型

定义泛型

​ 我们可以使用一个标识符,比如T(Type)在类中表示一种未知的数据类型

//在类上声明使用符号T,表示未知的类型
public class Point {
  private T x;
  private T y;
  //省略getter/setter
}

使用泛型

​ 一般在创建对象时,给未知的类型设置一个具体的类型,当没有指定泛型时,默认类型为Object类型。

//没有使用泛型,默认类型是Object
Point p1 = new Point();
Object x1 = p1.getX();
//使用String作为泛型类型
Point p2 = new Point();
String x2 = p2.getX();
//使用Integer作为泛型类型
Point p3 = new Point();
Integer x3 = p3.getX();

6.2、在集合中使用泛型

class ArrayList{
  public boolean add(E e){ }
  public E get(int index){ }
}

此时的E也仅仅是一个占位符,我们知道这里仅仅只是一个元素,但是不知道是什么类型,E表示元素(Element)的类型,那么当使用容器时给出泛型就表示,该容器只能存储某种类型的数据。

//只能存储String类型的集合
List list1 = new ArrayList();
list1.add("A");
list1.add("B");
//只能存储Integer类型的集合
List list2 = new ArrayList();
list2.add(11);
list2.add(22);

由于集合的前后两个类型必须相同,所以在JDK1.8之后就可以省略后面实例化对象的时候的泛型的书写

List list1 = new ArrayList();
// 可以简写为
List list1 = new ArrayList<>();

注意

List list = new ArrayList();//编译错误

List list = new ArrayList(); //编译错误

方法中,将数据类型作为参数传递,其实更像是一种数据类型的模板。

​ 如果不使用泛型的话,从容器中获取出来的元素,由于我们不知道类型,所以需要强制类型转换

6.2、自定义和使用泛型

定义泛型

​ 我们可以使用一个标识符,比如T(Type)在类中表示一种未知的数据类型

//在类上声明使用符号T,表示未知的类型
public class Point {
  private T x;
  private T y;
  //省略getter/setter
}

使用泛型

​ 一般在创建对象时,给未知的类型设置一个具体的类型,当没有指定泛型时,默认类型为Object类型。

//没有使用泛型,默认类型是Object
Point p1 = new Point();
Object x1 = p1.getX();
//使用String作为泛型类型
Point p2 = new Point();
String x2 = p2.getX();
//使用Integer作为泛型类型
Point p3 = new Point();
Integer x3 = p3.getX();

6.2、在集合中使用泛型

class ArrayList{
  public boolean add(E e){ }
  public E get(int index){ }
}

此时的E也仅仅是一个占位符,我们知道这里仅仅只是一个元素,但是不知道是什么类型,E表示元素(Element)的类型,那么当使用容器时给出泛型就表示,该容器只能存储某种类型的数据。

//只能存储String类型的集合
List list1 = new ArrayList();
list1.add("A");
list1.add("B");
//只能存储Integer类型的集合
List list2 = new ArrayList();
list2.add(11);
list2.add(22);

由于集合的前后两个类型必须相同,所以在JDK1.8之后就可以省略后面实例化对象的时候的泛型的书写

List list1 = new ArrayList();
// 可以简写为
List list1 = new ArrayList<>();

注意

List list = new ArrayList();//编译错误

List list = new ArrayList(); //编译错误