Java-数组

Java与C构造数组的语法有点不同,以下是从C到Java的个人理解。

C/C++与Java数组区别

对比学习:

以下仅考虑在堆栈的情况。
在C中,int arr[3];数组在栈上。Java中int[] arr = new int[3];在JVM虚拟内存堆上。
C中创建数组的语法,arr会在栈上空间分配一个地址,数组的地址不能被修改。
错误示范:int arr[3]; arr = NULL;
C数组在传参时,传递数组名会被解读成地址。

#include<cstdlib>
#include<cstring>
void test01()
{
    //Java数组在堆区创建,并会进行默认初始化.
    //以int[] 类型为例
    //对比C,Java数组类似C中的动态数组。
    int *arr = (int *)malloc(sizeof(int) * 10);
    memset(arr, 0, sizeof(int) * 10);
    free(arr);//需要手动释放
    //对比C++。
    int *arr2 = new int[10]();
    delete[] arr2;//手动释放。
}

数组

数组:相同类型元素的一个集合。内存中的一段连续的空间。Java数组在堆上,而不会在栈上。
int[] arr=int [3];在堆区上申请三个int类型连续空间。arr存储的是数组在堆区的地址。
arr把它理解为C指针(int *),arr存储数组首元素的低地址。
double[] array1=double [5];double类型的数组
String[] array2=String [3];字符串数组。

提炼语法。
T[] name = T [N];

  • T为数据类型,基本数据类型,引用数据类型。
  • name为数组名,将其理解为指向堆区空间数组首元素的指针。
  • N为数组元素个数。N可为常量,也可为变量。
    Java支持类似C中的变长数组,变量可以作为参数。
import java.util.Arrays;
import java.util.Scanner;
public class Example {
    public static void main(String[] args) {
        int n = 10;
        Scanner scanner = new Scanner(System.in);
        n = scanner.nextInt();//读取一个整数
        int arr[] = new int[n];//开变量n个int类型的字节大小
        System.out.println(Arrays.toString(arr));//打印数组。
    }
}
  1. C语言采用的语法是int arr[5];Java不支持这种写法,但Java支持int arr[]={1,2,3};这涉及下面的初始化问题。
动态初始化
  1. 由程序员指定堆区开多大的空间。
    int[] arr = new int[5];
    JVM会分配堆区的5个连续int大小的内存空间,把地址给arr。
  2. 可以分开写
    int[] arr; arr = new int[5];
    前面提过,把arr当成int*的指针理解。new int[5]这句话功能是在堆区开辟空间,和返回地址。arr实际上存储了这个地址。

Q:如果只单独创建了这块空间,并没有指针(准确来说是引用)接受这个值,会不会内存泄露?
不会,Java不同于C/C++,它可以自动管理堆区内存,当没有指针(引用)指向这块空间时,会自动回收内存。(后面会略微提及JVM内存分布)

  1. new 和 指定长度是动态初始化的标志。
  2. 数组元素值,为其类型默认值(如int默认为0)。

C中的malloc函数会在堆区开辟空间,但不会初始化。数组内部全是垃圾值。
calloc函数会进行初始化,将每个字节初始化为0.

  1. 注意事项
    Java中不会在数组声明左边指定长度。
    int[5] = new int[5];//错误写法

Java支持C的写法,但是int arr[5];错误写法,左边不能放长度。
int arr[]=new int[5];正确写法,Java风格的可以将类型与变量分离,清晰。

静态初始化

上面的动态初始化虽然叫初始化,但实际上是说明如何创建数组,因为我们不能自己设定值。

  1. 静态初始化特点是不用指定长度了
public class Example {
    public static void main(String[] args) {
       int[] arr= new int[]{1,2,3};//右值不能指定长度。数组长度由编译器决定。
    }
}
public class Example {
    public static void main(String[] args) {
        int[] arr = {1, 2, 3};//new int[]也省略了。
        int arr1[] = {1, 2, 3};//C语言风格
    }
}
```
2. 可以分开写吗?
`new int[]{1,2,3}`没明确指定长度,但编译器根据初始化,可以确定,且new表达式会返回地址。
分开写没问题。
```Java
public class Example {
    public static void main(String[] args) {
        //分开写没问题!
        int[] arr;
        arr = new int[]{1,2,3};

        //下面报错
        int[] arr2;
        arr2 = {1,2,3};
    }
}
```

`int[] arr2 = {1, 2, 3};`这句话new 和长度可以省略。但是分开写`  int[] arr2;
        arr2 = {1,2,3};`就不能省略了。
#### 空数组
Java可以创建空数组`int[] = new int[0];``String arr2[] = new String[0];`
空数组不存储任何元素。那么它与null有区别吗?空数组的内存怎么看?
先不提类与对象的概念,用C结构体迁移一下。
首先空数组与null不一样,数组创建在Java中是作为一个一个的对象。对象可以看作一个类似C的结构体变量,数组对象存储了自己的信息,比如存储了自己的类型,长度等等。
总而言之,Java中的数组可以近似看作C的结构体,里面有成员,成员有长度length,类型type,以及C类型的数组(这里存储的是数组的实际元素)。
空数组它有地址,而null是特殊字面量,表示不指向任何对象。

### 一维数组使用
简单回顾:
  数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 array.length-1。(array.length具体是啥见下文。)
1. 数组访问
与C一样,通过索引(index)和[]操作符来操作数组元素。
例如`int[] arr=new int[]{1,2,3};`
获取下标为2的数组元素.`int i = arr[2];`即获取数组第三个元素。
修改下标为2的数组元素。`arr[2] = 4;`
2. 遍历数组
还记得Java中的数组类似C中的结构体。Java数组有个字段length记录了数组的长度。
因此,获取数组长度`arr.length`。
```java
//遍历且打印一维数组。
for(int i = 0;i<arr.length;i++)
{
   System.out.print(arr[i]);
}
//反向遍历打印一维数组
for(int i = arr.length;i>=0;i++)
{
   System.out.print(arr[i]);
}
```
后面会介绍一种更快的方法,数组改成字符串打印。

3. for each循环
Java提供了一种很强的循环,我们处理数组的时候不在关心数组索引,而是针对每一个元素。
`for(variable : collection) statement`
variable是声明的变量 来依次代表数组的值,collection 现在认为数组。 statement 是代码语句。
for each循环,对于数组的每一个元素,用一个临时变量x表示整个集合。第一次循环x表示数组的第一个元素,每经历一次循环,x表示数组的下一个元素。
```java
  int[] array={1,2,3,4,5,6};
        for(int x: array)//x存储array数组的第一个到最后一个元素
        {
            System.out.println(x);
        }
```
如果你要在循环中要用到索引时,那么采用一般的for循环。
for each循环会遍历整个数组。

### Arrays类
更多内容翻JDK源码或者阅读API文档,其中大量方法都有很多重载形式。
Array类里面有很多操作数组的方法,具体怎么使用呢?
我先举个例子。
使用Arrays类前,需要在前面写`import java.util.Arrays;`
如果你一点不了解类与对象,那么我还是以C的结构体来迁移扩展。
#### 1. toString方法
Arrays类中的toString方法的作用是将数组的数据变成了字符串类型的数据。
原有数组没有改变,只是利用数组内部的数据构建返回了这么一个字符串。
```java
int[] array={1,2,3,4,5,6};
System.out.println(Arrays.toString(array));
//打印结果: [1, 2, 3, 4, 5, 6]
```
Arrays类视为一个结构体,不过这个结构体内部可以放函数的声明和定义。toString是Arrays类的一个函数,Array.toString就是调用Arrays结构体的toString函数,调用函数就要考虑传递参数的问题。`Arrays.toString(array);`将数组名作为此参数传递。
由打印结果可知:`[1, 2, 3, 4, 5, 6]`,该函数返回了左边的一个字符串。
* 模拟实现一下
```java
  public static String toString(int[] array)
    {
        if(array.length==0)
            return "[]";
       String s ="["+array[0];
       for(int i = 1;i<array.length;i++)
       {
           s+=", "+array[i];
       }
       s+="]";
       return s;
    }
    public static void main(String[] args) {
        int[] array={1,2,3,4,5,6};
        System.out.println(toString(array));
    }
```
toString方法重载,传递floatdouble类型的数组也可以。
感兴趣,可以翻一下Arrays类的toString源码。
关于toString方法现在了解这些足够了,后续出镜机会还很多。

#### 2. copyOf
copyOf方法原型`public static xxx[] copyOf(XXX [],int length);`
1. 第一个参数是要拷贝的数组副本。返回数组类型与第一个参数一致。
2. 第二个参数是数组长度。
```java
  public static void main(String[] args) {
        int[] array={1,2,3,4,5,6};
        int[] array1 = Arrays.copyOf(array,array.length);
        System.out.println(Arrays.toString(array1));
    }
```

3. 模拟实现copyOf方法
```java
    public static int[] copyOf(int[] array,int length)
    {
        int[] arr = new int[length];
        for (int i = 0; i < length; i++) {
            arr[i] = array[i];
        }
        return arr;
    }

    public static void main(String[] args) {
        int[] array={1,2,3,4,5,6};
        int[] array1 = copyOf(array,array.length);
        System.out.println(Arrays.toString(array1));
    }
```

#### 3.binarySearch(二分查找)
以下为回顾
* 顺序查找
```java
public static int find (int[] array,int key)
    {
        for (int i = 0; i < array.length; i++) {
            if(key == array[i])
            {
                return i;//找到了返回索引
            }
        }
        return -1;//找不到返回无效值(-1)
    }
```

* 二分查找
Arrays类提供快速查找有序数组的binarySearch方法。
`    public static int binarySearch(int[] a, int key);`
它会在int[] 数组中查找key元素的位置,如果查找成功则返回数组下标,否则返回-1;
```java
 public static void main(String[] args) {
        int[] array={1,2,3,4,5,6};
        System.out.println(Arrays.binarySearch(array , 5));//4
        System.out.println(Arrays.binarySearch(array,0));//-1
    }
```
模拟实现binarySearch
```java
    public static int binarySearch(int[] array , int key)
    {
        //数组必须有序,且为升序数组。
        int left = 0;int right = array.length;
        while(left<=right)
        {
            int mid = (right - left)/2+left;
            if(array[mid]>key)
            {
                right = mid - 1;
            }
            else if (array[mid]<key)
            {
                left = mid + 1;
            }
            else
            {
                return mid;
            }
        }
        return -1;
    }
    public static void main(String[] args) {
        int[] array={1,2,3,4,5,6};
        System.out.println(binarySearch(array , 5));//4
    }
```



#### 4. sort排序
以下是Arrays类的sort函数的一种重载形式
`public static void sort(int[] a);`
例子:
```java
 public static void main(String[] args) {
        int[] array={47,38,20,19,0,4,2};
        Arrays.sort(array);//传数组名
        //array被排成升序了,打印观察一下。
        System.out.println(Arrays.toString(array));
    }
//打印结果:[0, 2, 4, 19, 20, 38, 47]
```

冒泡排序
```java
public static void bubbleSort(int[] array)
{
    for (int i = 0; i < array.length; i++) {
        for (int j = 0; j < array.length - i - 1; j++) {
            if(array[j]>array[j+1])
            {
                int tmp = array[j];
                array[j] = array[j+1] ;
                array[j+1] = tmp;
            }
        }
    }
}
    public static void main(String[] args) {
        int[] array={47,38,20,19,0,4,2};
        bubbleSort(array);
        System.out.println(Arrays.toString(array));
    }
```

插入排序
```java
public static void insertSort(int[] array)
{
    for(int i = 1;i<array.length;i++){
        int tmp =array[i];//抽出来
        int end = i - 1;//记录上一个位置
        while(end>=0)
        {
            if(array[end]>tmp)
            {
                array[end+1]=array[end];//挪动位置
                end--;
            }
            else
            {
                break;
            }
        }
        array[end+1]=tmp;
    }
}

    public static void main(String[] args) {
        int[] array={47,38,20,19,0,4,2,-1,99,100,57};
        insertSort(array);
        System.out.println(Arrays.toString(array));
    }
```

#### equal方法
Arrays类提供了equal方法可以比较两个同类型的数组是否相等。
* 模拟实现及Arrays类的equals方法使用
```java
/* 
 * Main.java
 */
    public static boolean equals(int[] a , int[] a2)
    {
        //两个指向同一个堆区数组必然相等
        if(a==a2)
            return true;
        //其中一个为空引用必然不相等
        if(a==null||a2==null)
            return false;
        //比较长度
        int length = a.length;
        if(a2.length!=length)
            return false;

        //开始逐下标匹配
        for (int i = 0; i < length; i++) {
            if(a[i]==a2[i])
                ;
            else
               return false;
        }
        return true;
    }

    public static void main(String[] args) {
        int[] array = {1,2,3,4,5};
        int[] array2 = {1,2,3,4,5};
        System.out.println(Arrays.equals(array,array2));//调用Arrays类的方法
        System.out.println(equals(array,array2));//调用自己当前类实现的equal方法
    }
```





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值