五、数组高级
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<T> {
private T x;
private T y;
//省略getter/setter
}
复制代码
使用泛型
一般在创建对象时,给未知的类型设置一个具体的类型,当没有指定泛型时,默认类型为 Object 类型。
//没有使用泛型,默认类型是Object
Point 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<>();
复制代码
注意
泛型必须是引用类型,不能是基本数据类型
List<int> list = new ArrayList<int>();//编译错误
复制代码
泛型不存在继承的关系
List<Object> list = new ArrayList<String>(); //编译错误
复制代码
方法中,将数据类型作为参数传递,其实更像是一种数据类型的模板。
如果不使用泛型的话,从容器中获取出来的元素,由于我们不知道类型,所以需要强制类型转换
6.2、自定义和使用泛型
定义泛型
我们可以使用一个标识符,比如 T(Type)在类中表示一种未知的数据类型
//在类上声明使用符号T,表示未知的类型
public class Point<T> {
private T x;
private T y;
//省略getter/setter
}
复制代码
使用泛型
一般在创建对象时,给未知的类型设置一个具体的类型,当没有指定泛型时,默认类型为 Object 类型。
//没有使用泛型,默认类型是Object
Point 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<>();
复制代码
注意
泛型必须是引用类型,不能是基本数据类型
List<int> list = new ArrayList<int>();//编译错误
复制代码
泛型不存在继承的关系
List<Object> list = new ArrayList<String>(); //编译错误
复制代码
评论