写点什么

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

作者:XiaoLin_Java
  • 2022 年 1 月 12 日
  • 本文字数:6930 字

    阅读完需:约 23 分钟

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 数组只能存储初始化指定数量的元素,如果再多存储一个就报错:数组索引越界。此时就要考虑在保存操作时对数组做扩容操作。扩容的原理是:


  1. 创建一个原数组长度两倍长的新数组

  2. 把旧数组中的所有元素拷贝到新数组中

  3. 把新数组的引用赋给旧数组变量


 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<T> {  private T x;  private T y;  //省略getter/setter}
复制代码


使用泛型


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


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

6.2、在集合中使用泛型

class ArrayList<E>{  public boolean add(E e){ }  public E get(int index){ }}
复制代码


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


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


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


List<String> list1 = new ArrayList<String>();// 可以简写为List<String> list1 = new ArrayList<>();
复制代码


注意


  1. 泛型必须是引用类型,不能是基本数据类型


List<int> list = new ArrayList<int>();//编译错误
复制代码


  1. 泛型不存在继承的关系


List<Object> list = new ArrayList<String>(); //编译错误
复制代码


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


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

6.2、自定义和使用泛型

定义泛型


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


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


使用泛型


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


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

6.2、在集合中使用泛型

class ArrayList<E>{  public boolean add(E e){ }  public E get(int index){ }}
复制代码


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


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


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


List<String> list1 = new ArrayList<String>();// 可以简写为List<String> list1 = new ArrayList<>();
复制代码


注意


  1. 泛型必须是引用类型,不能是基本数据类型


List<int> list = new ArrayList<int>();//编译错误
复制代码


  1. 泛型不存在继承的关系


List<Object> list = new ArrayList<String>(); //编译错误
复制代码


用户头像

XiaoLin_Java

关注

问啥啥都会,干啥啥不行。 2021.11.08 加入

问啥啥都会,干啥啥不行。🏆CSDN原力作者🏆,🏆掘金优秀创作者🏆,🏆InfoQ签约作者🏆

评论

发布
暂无评论
Java 常用类大讲解!3️⃣(手写 API、源码必备)