第五章数组和字符串(暑假啃书自我提升计划action)

数组和字符串

一. 数组

1. 数组声明

  • 一个数组是相同数据类型的元素按一定顺序排序的集合

  • 数组中各元素的类型相同通过下标来访问数组中的元素,下标都可以从0开始

  • 在Java中,数组是对象。

  • 数组元素可以是基本数据类型,也可以是类类型和接口类型,还可以是数组

  • 数组在使用之前必须先声明

    • // 类型   数组名[]
      int intArray[];     // 基本数据类型
      
      Date dateArray[];   // 类类型
      
      • 类型是数组元素的类型
      • 数组名为合法的标识符
      • [ ]指明定义的是一个数组类型变量
  • 数组定义时并不会为数组分配内存,因此方括号 [ ] 中不需要指出数组元素的个数,即数组长度

  • 还可以使用另外一种格式声明数组

    • // 类型[]   数组名
      int[] num;
      Date[] dateArray;
      int intArray[5];	// java中没有静态的数组定义,这种写法是错误的
      

2. 创建数组

  • 初始化过程

    • 初始化过程就是数组的创建过程

    • 数组声明只是定义了一个数组引用,并没有为数组分配任何内存

    • 必须经过数组初始化,才能使用数组的元素

  • 数组的初始化分为两种

    • 静态初始化

      • 定义数组的同时给数组元素赋初始值

      • 使用一对大括号把初值括起来,每个元素对应一个引用

        int intArray[] = {1,2,3,4};     // 定义了一个含有4个元素的int型数组
        
        char charArray[] = {'a','b','c'};   // 定义了一个含有3个元素的char型数组
        
        String strArray[] = {"张三","李四","王五"};   // 定义了一个含有3个元素的字符串数组
        
    • 动态初始化

      • 以上述最后一个字符串数组为例,等价于下述语句

        String strArray[];
        
        strArray = new String[3];
        
        strArray[0] = "张三";
        strArray[1] = "李四";
        strArray[2] = "王五";
        
        • 此处进行的是动态初始化,使用运算符new为数组分配空间
        • 数组声明的方括号中表示数组元素的个数
    • 基本数据类型,其创建格式

      •  类型 数组名[] = new 类型[数组大小]
         类型[] 数组名 = new 类型[数组大小]
        
    • 类类型的数组创建

      • 使用new运算符只是为数组本身分配空间,并没有对数组的元素进行初始化

      • 所以对于类类型的数组,空间分配需要经过两步

      • 先创建数组本身

        • 类型 数组名[] = new 类型[数组大小]
          
      • 分别创建各个数组元素

        • 数组名[0] = new 类型[初值表]
          
      • 假设有一个类Point

        public class Point {
        
            int x,y;
        
            Point(int x1,int y1){
                x = x1;
                y = y1;
            }
        
            Point() {
                this(0,0);
            }
        
            public static void main(String[] args) {
                Point[] points = new Point[100];
                points[0] = new Point();
                points[1] = new Point();
                points[2] = new Point();
            }
        
        }
        
        • 语句Point[] points = new Point[100]; 只创建了100个Point型变量的数组,它并没有创建100个Point对象

        • 因为Point型是类类型,所以这些对象必须再单独创建

        •     points[0] = new Point();
              points[1] = new Point();
              points[2] = new Point();
          	........
          
      • Point型数组声明与数组创建之间的关系
        在这里插入图片描述

        • 执行Point[ ] points; 后,系统为变量points分配了一个引用空间如图A所示
        • 执行语句points = new Point[100]; 后,系统在内存中分配一个含100个原宿的数组对象,并把数组的首地址赋值给了points,如图B所示
        • 执行语句 points[1] = new Point(); 后,表示数组中有一个元素初始化了,数组的状态由图B转换图C
  • 数组变量的类型可以不同于所指向的数组类

    • Object[] Point;
      points = new Point[100];
      
      • points是Object类型的数组,第二行创建的数组时Point型的
      • Point派生于公共父类Object,反行之,是不被允许的

3. 访问数组元素

  • 数组中的元素个数length是数组类中唯一的数据成员变量

  • 数组下标从0开始,使用new创建数组时系统自动给length赋值,数组一旦创建完毕,其大小就固定了

  • 程序运行时可以使用length进行数组边界检查,如果发生越界,则会抛出异常

  • 创建一个有10个int型量的数组list,然后顺序分为访问每个元素

  • int list[] = new int[10];
    for (int i = 0; i < list.length; i++) {
        System.out.println(list[i]);
    }
    
    • 循环中使用的是 list.length ,而不是常数10,如此一来,不会引起数组下标越界异常
  • 定义了一个数组,并且new分配了内存空间后,就可以引用数组中的每一个元素了

    • 数组名加上下标就可以表示数组元素

    • 其中下标的访问可以是整型常数或者表达式

      // 数组名	[下标]
      list[i];
      
  • 当创建一个数组时,每个元素都被初始化

    Point[] points = new Point[100];
    
    • 它的每个值都被初始化为0,而数组points每个值都被初始化为null,表明还没有指向真正的Point对象,需要执行赋值语句

      • points[0] = new Point;
        
  • 任何变量都不能在没有初始化的状态下使用,编译器并不能检查数组的元素初始化情况,需要自己注意

  • 例子:给定一组整型数,求他们的平均值以及最大值

    public class Calculator {
    
        public static double calculatorAverage(int[] number) {
            
            int sun = 0;
            for (int i = 0; i < number.length; i++) {
                sun += number[i];
            }
            return sun / (double) number.length;
        }
    
        public static int findMaximun(int[] number) {
    
            int max = number[0];
            for (int i = 0; i < number.length; i++) {
                if (number[i] > max) {
                    max = number[i];
                }
            }
            return max;
        }
    
    }
    
    class Test{
        public static void main(String[] args) {
            int number[] = {23,33,54,98,3,5,29,22,72};
            System.out.println("平均值:"+Calculator.calculatorAverage(number));
            System.out.println("最大值:"+Calculator.findMaximun(number));
        }
    }
    
    
    控制台:
    平均值:37.666666666666664
    最大值:98
    

4. 多维数组

  • 多维数组的定义

    • 一般来说,n维数组就是n-1维数组的数组,例如 int[ ] [ ]是类型,它表示二维数组,每个数组是int类型的

    • 以二维数组为例,定义格式如下

      • 与一维数组一样,二维数组定义时对数组元素没有分配内存空间

      • 同样需要进行初始化之后,才可以访问每个元素

        // 类型   数组名[][]
        int intArray1[] [];
        
        // 另外两种方式
        int[] [] intArray2;
        int[] intArray3[];
        
  • 多维数组的初始化

    • 与一维数组一样,也分为动态和静态两种初始化方式

    • 静态初始化时,在定义数组的同时称为数组赋初值

      • int intArray[] [] = {{1,2},{3,4},{5,6}};
        
        • 无需指出数组每一维的大小,系统会根据初始化时给出的初值的个数自动计算每一维的大小
        • 外层括号所含各元素是数组第一维的各元素
        • 内层括号对应于数组第二维的元素
        • 上面定义的数组 intArray 是一个3行2列的数组
        • 使用两个下标可以访问数组中的对应元素,如 intArray[1] [1] 表示该数组第二行第二列的元素5
        • 数组各维的下标均从0开始
    • 二维数组进行动态初始化时,有两种分配内存空间的办法

      • 直接分配

        • 就是直接为每一维分配空间,声明数组时,给出维度的大小

        • // 类型 数组名 [] [] = new 类型[第一维大小][第二维大小];
          int a[] [] = new int[2][3];  // 声明了一个2行3列的二维数组
          
      • 按维分配

        • 必须从最高维起,分别为每一维分配内存

        • // 类型 数组名 [] [] = new 类型[第一维大小][;
          int a[] [] = new int[4][];	// 声明了一个2行的字符串数组
          a[0] = new int[5];	// 表明了第一行有5列
          a[1] = new int[5];	// 表明了第二行有5列  套娃即可
          a[2] = new int[5];
          a[3] = new int[5];
          
          a[0][1] = new int[2]	// 给第一行第二列赋值1
          
          
          • 第一行的声明语句调用new了一个数组对象,指定第一维的大小4,此时数组的4个元素中各含有一个null引用
          • 后续的4个声明语句分别让着4个元素指向各含有5个元素的一维数组
          • 构成一个4行5列的二维数组
          • 创建数组时,第二维的大小是可以不一样的,创建一个非矩阵数组
      • 维数声明应从高维到低维

      • 数组的维数指定只能出现在new运算符之后

  • 多维数组的引用

    • 在定义并初始化多维数组之后,可以使用多维数组中的每个元素,以二维数组为例,引用方式如下:

      • 数组名[第一维下标] [第二维下标]

      • 数组下标也称为索引,都是从0开始。第一维称为行,第二维称为列,如下:

      • int myTable[][] = new int[4][3];
        
        • 如果要访问 myTable 的元素,只需要指定相应的行,列下标即可
  • 二维数组使用示例

    public static void main(String[] args) {
    
        int myTable[][] = {
                {23,45,34,65,78,54,12},
                {2,5,4,6,77,55,32},
                {21,49,37,25,18,54,12},
                {20,85,94,26,88,44,99},
        };
    
        for (int row = 0; row < 4; row++) {
            for (int col = 0; col < 7; col++) {
                System.out.print(myTable[row][col]+" ");
            }
            System.out.println();
        }
    
    }
    
    控制台:
    23 45 34 65 78 54 12 
    2 5 4 6 77 55 32 
    21 49 37 25 18 54 12 
    20 85 94 26 88 44 99 
    
  • 维数组也有length属性,但只表示第一维的长度,例如

    • ages.length 的值是4,而不是28,也不是7

    • int[][] ages = new int[4][7];
      int[] firstArray = ages[0];
      System.out.println(ages.length);    // 4
      System.out.println(firstArray.length);    // 7
      
  • 在Java中,数组时用来表示同以类型数据的数据结构,初始化之后,数组的大小不会再动态变化

    • 数组变量是一个指向数组对象实例的引用

    • 虽然不能再改变它的大小,但是可以使用同一个引用变量指向另一个全新的数组,如下:

      • int elements[] = new int[6];
        elements = new int[10];
        
      • 执行这两段语句后,elements指向了第二个数组

      • 第一个数组实际上丢了,除非还有其他引用指向它

  • System类中提供了一个arraycopy( )方法,可以高效实现数组之间的复制

    • 数组复制例子

    • 数组element 和 hold 作为方法 arraycopy 的参数使用,当数组作为函数参数时,时将数组引用穿给方法,方法中对数组内容的改变都会对方法外有影响

      public static void main(String[] args) {
      
          int element[] = {1,2,3,4,5,6};  // 初始数组
          int hold[] = {10,9,8,7,6,5,4,3,2,1};   // 更大的另一个数组
      
          // 把element数组中的所有元素复制到hold数组中,下标从0开始
          System.arraycopy(element,1,hold,2,4);
      
      }
      
      • element是一个含6个int型的数组,hold含有10个int型
      • 最后一行的含义是:把element中下标1开始的4个元素,依此放到hold下标从2开始的位置
      • element中的2,3,4,5 替换 hold中的 8,7,6,5
  • Java.util.Arrays中为数组提供了一系列静态方法

    • equals(type[ ],type[ ])

      • 可用来判定type类型的两个数组的值是否相同
      • type可以是基本类型,也可以是类类型,如果相等,则返回true
    • 如果数组元素为对象,可以调用对象的equals方法来判断是否相同

    • sort(type[ ])将type类型的数组按照升序排序

    • 如果数组元素为对象,可以调用对象的compareTo( )来得到比较结果

二. 字符串类型

1.字符串的声明

  • Java.lang中封装了String 和StringBuffer类,可以方便的处理字符串

    • String处理不变字符串
    • StringBuffer类用处理可变字符串
  • 字符串是内存中连续排列的0个或者多个字符

  • 不变字符串是指字符串一旦创建,其内容就不可改变

  • 比如:对String类的实例进行查找,比较,连接等操作,既不能输入新字符,又不能改变字符串的长度

  • Java中字符串分为常量和变量

    • 字符串常量是用双引号括起来的一串字符

    • 系统为程序中出现的字符串常量自动创建一个String对象

      • System.out.println("Hello World");
        
        • 这个"Hello World"对象,这个创建过程是隐含的
  • 对于字符串变量,在使用之前要显式声明,并进行初始化

    • StringBuffer 类不能使用字符串常量来创建
    • char是单字符 String是字符串
    public static void main(String[] args) {
        
        String s1;
        StringBuffer sb1;
        // 也可以创建一个空的字符串
        String s2 = new String();
        StringBuffer sb2 = new StringBuffer();
        
        // 此外还可以由字符串数组创建字符串  char是单字符  String是字符串
        char chars[] = {'a','b','c'};
        String s3 = new String(chars);
        
        // 直接使用字符串常量来初始化一个字符串
        String s4 = "Hello World";
        
        // StringBuffer类不能使用字符串常量来创建
        StringBuffer sb3 = "Hello World";   // 报错
        
        // 可以使用String类的变量来创建,比如使用已经定义了的s4
        StringBuffer sb4 = new StringBuffer(s4);
    
    }
    

2. 字符串的操作

  • String类的对象实例时不可改变的一旦创建就确定下来

  • 字符串施加操作后并不会改变字符串本身,而是又生成了另外一个实例

  • StringBuffer类处理可变字符串,当修改一个StringBuffer类的字符串时,不是再创建一个新的字符串对象而是直接操作原字符串

  • String 类和 StringBuffer 类共有的常用方法如下

    • length();       // 返回字符串的长度,即字符个数
      
      charAt(int index);  // 返回字符串中index位置的字符
      
      subString(int beginIndex);  // 截取当前字符串中从beginIndex开始到末尾的子串
      
      replace(char oldChar,char newChar); // 将当前字符串中出现的所有oldChar转换为newChar
      
      toLowerCase();  // 将当前字符串中所有的字符转换为小写
      
      toUpperCase();  // 将当前字符串中所有的字符转换为大写
      
      startsWith(String prefix);  // 测试prefix是否是当前字符串的前缀
      
      concat(String str);     // 将str连接在当前字符串的尾部
      
      trim();         // 去掉字符串前面以及后面的空白
      
      valueOf(type value);    // 将type类型的参数value转换为字符串形式
      
  • StringBuffer 类中常用方法如下

    • append(String str);     // 将参数str表示的字符串添加到当前串最后
      
      replace(int start, int end, String str);    // 使用给定的str代替从start到end之间的子串
      
      capacity();     // 返回当前的容量
      
  • String类的字符串连接还可以使用运算符+来实现

  • 系统为String类对象分配内存的时候,按照对象中所含字符的实际个数等量分配

  • StringBuffer,除去字符所占空间之外,另加16个字符大小的缓冲区

  • 对于StringBuffer类对象,length( )方法获取的是字符串的长度

  • capacity( ) 方法返回当前的容量,即字符串长度加上缓冲区的大小

  • 字符串操作实例:

    • String s = "Hello World hi lcy";
      System.out.println("没有变化前:s="+s);       // s=Hello World hi lcy
      
      String t = s.toLowerCase(); // 转换为小写
      System.out.println("变化后:t="+t);     // t=hello world hi lcy
      
      StringBuffer strb = new StringBuffer(s);
      System.out.println("字符串的长度,s="+s.length());     // s=18
      System.out.println("StringBuffer字符串的长度,strb="+strb.length());   // strb=18
      
      t = s.replace('l','t');
      System.out.println("转换后t="+t);      //  t=Hetto Wortd hi tcy
      
      StringBuffer strb2 = strb.replace(0, 4, "goods");
      System.out.println("替换之后:strb="+strb2);     // strb=goodso World hi lcy
      
      System.out.println("返回当前的容量:strb="+strb.capacity());    // 34
      
  • Java中可以使用关系运算符 == 来判断两个字符串是否相等,与equals( ) 方法不同的是,== 判断两个字符串对象是否是同一个实例,就是它们在内存中的存储空间是否相等

    • 字符串比较例子:

      String s1 = "Hello";
      String s2 = "Hello";
      String s3 = new String("Hello");
      String s4 = new String(s1);
      String s5 = s1;
      
      System.out.println(s1.equals(s2));  // true
      
      System.out.println(s1==s2); // true
      
      System.out.println(s1.equals(s3));  // true
      
      System.out.println(s1==s3);     // true
      
      System.out.println(s1.equals(s4));  // true
      
      System.out.println(s1==s4);     // false
      
      System.out.println(s1.equals(s5));  // true
      
      System.out.println(s1 == s5);  // true
      
      • 如上程序中,s1和s2使用相同的字符串常量来定义,s1与s2都指向这同一个常量,所以使用 == 或者equals方法来判定,结果都是为true的

      • 而s3是使用字符串常量创建的另一个实例,虽然与s1的字符串相同,但却不是相同的实例,使用 == 判断内存地址的时候是不相等的,与s4一样,都是另外一个实例

      • 而s5和s1指向同一个实例,所以在两种方式下比较都是相同的,指向同一个实例的两个引用互称为别名

      • 内存地址相同说明这俩是同一个东西,所以hashcode一定相同。hashcode相同,两个对象不一定相同

        • 在hash类的存储结构中,先计算hashcode决定其在数组中的位置,然后遍历数组上的链表,对比各个元素hashcode是否有重复,如果有重复则调用equals方法对比元素是否相同,如果hashcode和equals都相同,则认为元素相同,则不再添加或更新原来的
      • equals在未被重写的时候默认调用Object里的equals,这时的equals其实就是==

三. Vector类

1. 概述

  • 数组只能保存固定数目的元素,数组空间一经过申请就不可再改变,为了解决这个问题**,引入了Vector可变数组(向量)**

  • Vector类似于数组,可以使用整数下标来访问各个元素,比数组功能更加强大

    • Vector的长度可以根据需要来改变
    • 保存的元素类型也可以不一样
  • 当需要处理数目不定,类型不同或者是需要频繁地在对象序列中增删查操作时,通过使用Vector来代替数组

    • Vector类的实例中只能保存对象类型,不能是基本数据类型
  • Vector类包含的成员变量有三个

    • protected Object[] elementData; // 增量的大小,如果值为0,则缓冲区大小每次倍增
      
    • protected int elementCount;	// Vector对象中元素的数量
      
    • protected int capacityIncrement;	// 元素存储的数组缓冲区
      
      • 系统内部会记录Vector类实例的容量capacity,实际保存的元素个数由 elementCount 来记录,这个值不能大于容量值
      • 当有元素加入到向量时,elementCount 会相应增大,当向量中添加的元素超过了它的容量之后,向量的存储空间以容量增值 capacityIncrement 的大小为单位增长,为新的元素加入做好准备。
      • 元素保存在数组 elementData 中

2. Vector类的方法

  • 使用Vector一定要先创建再使用
    • 如果不先使用new运算符利用构造方法创建对象,可能造成堆栈移除或者使用空指针移除
a. 构造方法
  • 有3个常用的构造方法

    • public Vector() {	// 构造一个空向量
          this(10);	// 标准容量为10
      }
      
    • public Vector(int initialCapacity) { // 指定的初始容量构造一个空向量
          this(initialCapacity, 0);
      }
      
    • public Vector(int initialCapacity, int capacityIncrement) {
          super();
          if (initialCapacity < 0)
              throw new IllegalArgumentException("Illegal Capacity: "+
                                                 initialCapacity);
          this.elementData = new Object[initialCapacity];
          this.capacityIncrement = capacityIncrement;
      }
      
      • 以指定的存储容量 initialCapacity 和 容量增量 capacityIncrement 构造一个空向量
  • 创建Vector实例时,要指明其中保存的元素类型,如下:

    Vector<String> myVector = new Vector<>(100,50);
    
    • 创建的 myVector 向量序列初始有100个字符串空间
    • 以后一旦用尽以50为单位递增,使序列中能够容纳元素个数为150,200…
b. 添加方法
  • 往Vector类对象中添加元素的常用方法如下

    • public synchronized void addElement(E obj) { // 将新元素obj添加到序列尾部
          modCount++;
          ensureCapacityHelper(elementCount + 1);
          elementData[elementCount++] = obj;
      }
      
    • public synchronized void insertElementAt(E obj, int index) { 
          modCount++;
          if (index > elementCount) {
              throw new ArrayIndexOutOfBoundsException(index
                                                       + " > " + elementCount)java;
          }
          ensureCapacityHelper(elementCount + 1);
          System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
          elementData[index] = obj;
          elementCount++;
      }
      
      • 将指定对象obj插入到指定位置index处
    • public void add(int index, E element) {  // 在向量的指定位置index插入指定的元素obj
          insertElementAt(element, index);
      }
      
      
      
    • public synchronized void insertElementAt(E obj, int index) { 
          modCount++;
          if (index > elementCount) {
              throw new ArrayIndexOutOfBoundsException(index
                                                       + " > " + elementCount)java;
          }
          ensureCapacityHelper(elementCount + 1);
          System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
          elementData[index] = obj;
          elementCount++;
      }
      
      • 将指定对象obj插入到指定位置index处
    • public void add(int index, E element) {  // 在向量的指定位置index插入指定的元素obj
          insertElementAt(element, index);
      }
      
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值