第四章——流程控制与数组

Java学习笔记

第四章——流程控制与数组

条件结构

  • if控制的条件结构与switch控制的条件结构与C语言完全相同。

循环结构

  • for循环结构,while循环结构,do while循环结构也和C语言相同。
  • 使用break结束循环:正常用法与C语言相同,即在循环内加入break;当程序执行这一条命令时,将跳出该层循环;但Java中break还可以在后面加入一个标签,如下所示:
public class BreakTest {
    public static void main (String[] args){
        outer:
        for (int i = 0 ; i < 10 ; i++){
            System.out.println("i的值为:"+i);
            for (int j  = 0 ; j < 3 ; j++){
                System.out.println("j的值为:"+j);
                if(j == 1)
                    break outer;
            }
        }
    }
}
  • 执行结果如下:
i的值为:0
j的值为:0
j的值为:1
  • 同样的,continue也有与其相同的用法。

数组类型

  • 数组元素的类型是唯一的,即一个数组里只能存储一种数据类型的数据。
定义数组
  • Java语言支持两种语法格式来定义数组
type[] arrayName;
type arrayName[];
  • 推荐使用第一种方式,变量类型就是type[],而变量名则是arrayName

注意:定义数组时不能指定数组的长度。

  • Java语言中数组必须先初始化,然后才可以使用。所谓的初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值
  • 数组的初始化有两种方式:
    1. 静态初始化:初始化时由程序员显示指定每个数组元素的初始值,由系统决定数组长度
    2. 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。
静态初始化
  • 静态初始化的语法格式如下:
arrayName = new type[] {element1,element2,element3...}
  • 在上面的语法格式中,前面的type就是数组元素的数据类型,此处的type必须与定义数组变量时所使用的type相同,也可以是定义数组时所制定的的type子类,并使用花括号把所有的数组元素括起来,多个数组元素之间以英文逗号隔开,定义初始化的花括号紧跟在[]之后。值得指出的是,执行静态初始化时,显式指定的数组元素值的类型必须与new关键字后的type类型相同,或者是其子类的实例。下面代码定义了使用这三种形式来进行静态初始化:
public class ArrayTest {
    public static void main(String[] args){
        int[] intArr;
        intArr = new int[] {5,6,7,8};

        Object[] objArr;
        objArr = new String[] {"lancibe", "java"};
        Object[] objArr2;
        objArr2 = new Object[] {"lancibe","java"};
    }
}
  • 因为Java语言是面向对象的编程语言,能很好的支持子类和父类的继承关系:子类实例是一种特殊的父类实例。在上面的程序中,String类型是Object类型的子类,即字符串是一种特殊的Object实例。
  • 除此之外,静态初始化还有如下简化的语法形式:
type[] arrayName = {element1, element2, element3...};
  • 这其实就是将数组定义和数组初始化同时完成
动态初始化
  • 动态初始化只指定数组的长度,由系统为每个数组元素指定初始值。动态初始化的语法格式如下:arrayName = new type[length]
  • 在上面的语法中,需指定一个int类型的length参数,这个参数指定了数组的长度,也就是可以容纳数组元素的个数。
  • 执行动态初始化是,程序员指定了数组的长度,系统按如下规则分配初始值
    • 整数类型:0
    • 浮点类型:0.0
    • 字符类型:’\u0000’
    • 布尔类型:false
    • 引用类型:null
  • 要注意不要同时使用静态初始化和动态初始化,也就是说,不要在进行数组初始化时,既指定了数组的长度,也为每个数组元素分配初始值。
使用数组
  • 数组最常用的用法就是访问数组元素,包括对数组元素进行赋值和取出数组元素的值。访问数组元素都是通过在数组引用变量后紧跟一个方括号[],方括号里是数组元素的索引。索引是从0开始的。
foreach循环
  • Java提供了一种简单的循环:foreach循环。这种循环遍历数组和集合(集合在后面会学到),无需获得数组和集合的长度,无需根据所引来访问数组元素和集合元素,foreach循环会自动遍历数组和集合中的每一个元素。
  • foreach循环的语法格式如下:
for (type variableName : array | collection)
{
	//variableName 自动迭代访问每个元素
}
  • 下面程序将示范如何使用foreach循环来遍历数组元素
public class ForEachTest {
    public static void main(String[] args){
        String[] names = {"lancibe", "123", "linux"};

        //其中的name将会自动迭代每个元素
        for (String name:names) {
            System.out.println(name);
        }
    }
}
  • 通常不要对循环变量进行赋值,没有什么意义,且极容易引起错误。

深入数组

内存中的数组
  • 数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可以通过该数组变量来访问数组元素
  • 与所有引用变量相同的是,引用变量是访问真实对象的根本方式。也就是说,如果希望在程序中访问数组对象本身,则只能通过这个数组的引用变量来访问它。
  • 实际的数组对象被存储在堆(heap)中,如果引用该数组对象的数组引用变量是一个局部变量,那么他被存储在栈(stack)内存中。
  • 如果堆内存中数组不再有任何引用变量指向自己,则这个数组将成为垃圾,该数组所占的内存将会被系统的垃圾回收机制回收。因此,为了让垃圾回收机制回收一个数组所占的内存空间,可以将该数组变量赋值为null,也就切断了数组引用变量和实际数组之间的引用关系,实际的数组也就成了垃圾。
  • 只要类型相互兼容,就可以让一个数组变量指向另一个实际的数组,这种操作会让人产生数组的长度可变的错觉。如下程序所示
public class ArrayInRam {
    public static void main(String[] args){
        int[] a = {5, 7, 20};
        int[] b = new int[4];
        System.out.println("b的长度为" + b.length);

        for(int temp : a){
            System.out.println(temp);
        }

        for(int temp : b){
            System.out.println(temp);
        }

        b = a;
        System.out.println("b的长度为" + b.length);
    }
}
  • 运行该程序后,发现先输出b的长度为4,然后依次遍历a和b,最后输出了b的长度为3。看起来数组长度是可变的,但其实,只是b的指向变了,也就是原来a的引用的数组现在有了两个引用,而原来b引用的数组现在失去引用,变成垃圾。
引用类型数组的初始化
  • 引用类型数组的数组元素是引用,因此情况更为复杂。每个数组元素里存储的还是引用,指向另一块内存,这块内存里存储了有效数据。
  • 为了更好的说明引用类型数组的运行过程,下面先定义一个Person类(所有类都是引用类型)。Person类代码如下
class Person{
    public int age;
    public double height;

    public void info(){
        System.out.println("我的年龄是:"+age+",我的身高是:"+height);
    }
}
  • 下面程序将定义一个Person[]数组,接着动态初始化这个Person[]数组,并为这个数组的每一个元素指定值。
class Person{
    public int age;
    public double height;

    public void info(){
        System.out.println("我的年龄是:"+age+",我的身高是:"+height);
    }
}
public class ReferenceArrayTest {
    public static void main(String[] args){
        Person[] students;
        students = new Person[2];

        Person zhang = new Person();
        zhang.age = 15;
        zhang.height = 158.;

        Person lee = new Person();
        lee.age = 16;
        lee.height = 161.;

        students[0] = zhang;
        students[1] = lee;

        lee.info();
        students[1].info();
    }
}
  • 可以发现,执行结果完全一样,这说明,lee和student[1]指向同一个内存区,引用了同一个Person对象,也具有相同的效果。
  • 对于多维数组,在进行初始化时,可以只指定最左边维的大小、也可以一次指定每一维的大小。二维数组是一维数组,其数组元素是一维数组。
Java 8 增强的工具类:Arrays
  • Java提供的Arrays类里包含的一些static修饰的方法可以直接操作数组,这个Arrays类里包含如下几个static修饰的方法(static修饰的方法可以直接通过类名调用)
int binarySearch(type[] a, type key):使用二分法查询key元素值在数组a里出现的索引。若数组不包含key元素值,则返回负数,调用该方法时要求数组中元素已经按序排列,这样才能得到正确结果。
int binarySearch(type[] a, int fromIndex, int toIndex, type key):与前一个方法相似,不过只搜索fromIndex到toIndex索引之间的排好序的元素。
type[] copyOf(type[] original, int length):这个方法会把original数组复制成一个新数组,其中length是新数组的长度。如果length小于original数组的长度,这新数组就是原数组的前length个元素;如果length大于original.length,则后面自动补充0或null
type[] copyOfRange(type[] original, int from, int to):这个方法与前面相似,但是只拷贝from索引到to索引的元素。
boolean equals(type[] a, type[] a2):如果两数组长度和元素完全相同,则返回true
void fill(type[] a, type val):将a的所有元素赋值为val
void fill(type[] a, int fromIndex, int toIndex, type val):懂的都懂^^
void sort(type[] a):该方法对a数组的数组元素进行排序
void sort(type[] a, int fromIndex, int toIndex)
String toString(type[] a):该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号和空格来隔开
  • 下面示范了Arrays类的用法
import java.util.Arrays;

public class ArraysTest {
    public static void main(String[] args){
        int[] a = new int[]{3, 4, 5, 6};
        int[] a2 = new int[] {3, 4, 5, 6};

        System.out.println("a和a2是否相等:"+ Arrays.equals(a, a2));

        int[] b = Arrays.copyOf(a, 6);
        System.out.println("b的数组元素为:"+Arrays.toString(b));

        Arrays.fill(b, 2, 4, 1);
        System.out.println("b的数组元素为:"+Arrays.toString(b));

        Arrays.sort(b);
        System.out.println("b的数组元素为:"+ Arrays.toString(b));

    }
}
  • 输出结果是
a和a2是否相等:true
b的数组元素为:[3, 4, 5, 6, 0, 0]
b的数组元素为:[3, 4, 1, 1, 0, 0]
b的数组元素为:[0, 0, 1, 1, 3, 4]

Arrays类处于java.util包下,为在程序中使用Arrays类,必须在程序中导入java.util.Arrays类,如该程序第一行所示。

  • 除此之外,在System类里也包含了一个static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)方法,该方法可以将src数组里的元素值赋给dest数组的元素,其中srcPos指定从数组的第几个元素开始赋值,length参数指定将src数组的多少个元素值赋给dest数组的元素。

  • Java 8 增强了Arrays类的功能,为Arrays类提供了一些工具方法,这些工具方法可以充分利用CPU并行能力来提高设值、排序的技能。下面是Java 8 为Arrays类增加的工具方法。

void parallelPrefix(xxx[] array, XxxBinaryOperator op):该方法使用op参数指定的计算公式计算得到的结果作为新的元素,op计算公式包括left、right两个形参,其中left代表数组中前一个索引处的元素,right代表数组中当前索引处的元素,当计算第一个新数组元素时,left的值默认为1
void parallelPrefix(xxx[] array, int fromIndex, int toIndex, XxxBinatyOperator op):该方法与上相似
void setAll(xxx[] array, IntToXxxFunction generator):该方法使用指定的生成器(generator)为所有数组元素设置值,该生成器控制数组元素的值的生成算法。
void parallelSetAll(xxx[] array, IntToXxxFunction generator):增加并行能力,利用多CPU并行来提高性能
void parallelSort(xxx[] a)
void parallelSort(xxx[] a, int fromIndex, int toIndex)
Spliterator.OfXxx spliterator(xxx[] array):将该数组的所有元素转换成对应的Spliterator对象
Spliterator.OfXxx spliterator(xxx[] array, int startInclusive, int endExclusive):仅转换startInclusive到endExclusive索引的元素
XxxStream stream(xxx[] array):该方法将数组转换为Stream,Stream是Java 8 新增的流式编程的API。
XxxStream stream(xxx[] array, int StartInclusive, int endExclusive)
  • 上面方法列表中,所有以parallel开头的方法都表示该方法可利用CPU并行的能力来提高性能。上面方法中的xxx代表不同的数据类型,比如处理int[]型数组时应将xxx换成int,处理long[]型的数组时应将xxx换成long。
  • 下面程序示范了Java 8 为Arrays类新增的方法。
import java.util.Arrays;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;

public class ArraysTest2 {
    public static void main(String[] args){
        int[] arr1 = new int[] {3, -4, 25, 16, 30, 18};
        Arrays.parallelSort(arr1);

        System.out.println(Arrays.toString(arr1));
        int[] arr2 = new int[] {3, -4, 25, 16, 30, 18};
        Arrays.parallelPrefix(arr2, new IntBinaryOperator() {
            public int applyAsInt(int i, int i1) {
                return i*i1;
            }
        });
        System.out.println(Arrays.toString(arr2));

        int[] arr3 = new int[5];
        Arrays.parallelSetAll(arr3, new IntUnaryOperator() {
            public int applyAsInt(int i) {
                return i*5;
            }
        });
        System.out.println(Arrays.toString(arr3));
    }
}
  • 输出结果如下:
[-4, 3, 16, 18, 25, 30]
[3, -12, -300, -4800, -144000, -2592000]
[0, 5, 10, 15, 20]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lanciberrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值