APCSA数组的基础知识和常见方法

数组的基础知识

  • 数组的创建
  • 列表元素的修改
    • 普通 for 循环遍历数组
    • for-each loop 遍历数组
  • 逆序遍历数组
    • 用 for-each 循环求数组最大值
    • 求数组最小值
  • 返回最大值的位置
  • 返回最小值的位置
  • 查找元素
  • 检查元素是否在数组中
  • for-each 检查元素是否在数组中
  • 字符串的比较
    • 字符串比较 == 比较内存地址
    • euqals 比较内容
  • 字符串字面量
  • 自定 equals 方法比较对象
    • Point 类比较
  • 自定义 equals 的比较逻辑

数组的创建

数组是就是,一系列的值;数组创建之后大小不能变化。
ArrayList 也叫做动态数组,创建之后可以动态增删元素。

int[] arr = new int[3]; // 默认值 0 String[] colors = new String[4]; // 默认值 null double[] c = new double[4]; // 默认值是 0.0 boolean[] bs = new boolean[4]; // false 

new 关键字表示分配新的内存空间来保存数据。

列表元素的修改

int[] arr = {1, 2}; // 直接指定元素 String[] list = {"a", "b"};
list[1] = "lucy"; // 就是修改 list 列表的第二个值 
lucy

普通 for 循环遍历数组

// 遍历数组 for (int i = 0; i < list.length; i++)
{
    System.out.println(list[i]);
}
a
lucy

for-each loop 遍历数组

// for-each loop for (String s : list)
{
    System.out.println(s);
}
a
lucy
int[] arr = {1, 2, 3, 4, 6, 10, 20};

遍历数组是最基本的操作,但是有时候需要按照相反的顺序遍历数组

for (int i = arr.length - 1; i >= 0; i--)
{
  System.out.println(arr[i]);
}
20
10
6
4
3
2
1

逆序遍历数组

有的同学对于arr.length - 1不理解,是这样,数组中的元素索引是从 0 开始的,所以一个数组如果有 3 个元素,索引就是 0 1 2,所有最后一个元素的索引是数组的长度减去 1.

public static void reverseDisplay(int[] arr) { for (int i = arr.length - 1; i >= 0; i--)
    {
      System.out.println(arr[i]);
    }
}
reverseDisplay(arr)
20
10
6
4
3
2
1

用 for-each 循环求数组最大值

/** 返回最大值 */ public static int maxForEachLoop(int[] arr) { int max = arr[0]; // 假设,第一个元素是最大的 for (int v : arr)
    { if (v > max) // 如果遇到更大的值,就更新最大值 {
            max = v;
        }
    } return max;
}

熟悉了 for-each 循环之后,会发现,这种写法其实更加简洁。

/** 返回最大值 */ public static int max(int[] arr) { int max = arr[0]; // 假设,第一个元素是最大的 for (int i = 0; i < arr.length; i++)
    { if (arr[i] > max)
        {
            max = arr[i];
        }
    } return max;
}

很明显,利用 regular for loop 要比 for-each loop 稍微麻烦一点。

求数组最小值

/** 返回最小值 */ public static int minForEachLoop(int[] arr) { int min = arr[0]; // 假设,第一个元素是最小的 for (int v : arr)
    { if (v < min)
        {
            min = v;
        }
    } return min;
}
max(arr)
20
min(arr)
1

上面的方法是求数组中元素的最大值和最小值,我们用 for-each 可以简化代码,但是有时候我们想要知道,最大值的索引,就得用普通 for 循环了。

返回最大值的位置

// 返回最大值的位置 public static int posOfMaxValue(int[] arr) { int maxIndex = 0; // 假设最大值的索引是 maxIndex for (int i = 0; i < arr.length; i++)
    { if (arr[i] > arr[maxIndex])
        {
            maxIndex = i;
        }
    } return maxIndex;
}
int[] arr = {1, 2, 3, 4, 6, 10, 20};
posOfMaxValue(arr)
6

这个方法返回的是最大值在数组中的索引,而不是最大值本身。在有的算法,比如选择排序中,我们往往需要知道在某些元素中,最大值的位置,就可以用到这个方法。

返回最小值的位置

// 返回最大值的位置 public static int posOfMinValue(int[] arr) { int minIndex = 0; // 假设最大值的索引是 maxIndex for (int i = 0; i < arr.length; i++)
    { if (arr[i] < arr[minIndex])
        {
            minIndex = i;
        }
    } return minIndex;
}
int[] arr = {1, 2, 3, 4, 6, 10, 20};
posOfMinValue(arr)
0

查找元素

查找一个元素在数组中的位置,如果不存在就返回 -1;如果存在返回这个元素的索引。
比如说,字符串有indexOf(String s)方法,查找字符串 s,在另外一个字符串第一次出现的位置;如果字符串 s 在另外一个字符串中没有出现,那么就返回 -1。我们可以自定义数组的方法,名称是 find

/** 查找一个值 value 在数组 arr 中的位置;如果不存在返回 -1*/ public static int find(int[] arr, int value) { for (int i = 0; i < arr.length; i++)
    { if (arr[i] == value)
        { return i; // 如果找到了直接返回位置 }
    } return -1; // 如果找不到返回 -1 }
int[] arr = {2, 3, 1};
find(arr, 2)
0
int[] arr = {2, 3, 1};
find(arr, 3)
1
find(arr, 100)
-1

检查元素是否在数组中

我们有的时候并不需要知道一个元素在一个数组中的位置,只是要知道元素是否在数组中存在即可,这个时候可以用普通 for 循环来做;要求返回 true 或者 false。
有的同学,我可以调用上面的 find 方法,然后把结果跟 -1 比较,就能判断在不在了,但是这种方法相对繁琐,我们可以直接定义方法,判断一个元素是否在数组中出现。这类方法的方法名可以是 find,in,contain,have。

/** 查找一个值 value 在数组 arr 中的位置;如果不存在返回 -1*/ public static boolean have(int[] arr, int value) { for (int i = 0; i < arr.length; i++)
    { if (arr[i] == value)
        { return true; // 如果找到了直接返回位置 }
    } return false; // 如果找不到返回 -1 }
int[] arr = {2, 3, 1};
have(arr, 200)
false 
have(arr, 3)
true 

上面的代码用的是普通 for 循环,但是我们这次不关心 value 在数组中的位置,只关心有没有。既然这样的话,我就可以使用 for-each 循环来简化代码,减少代码出错的机会。

for-each 检查元素是否在数组中

public static boolean have(int[] arr, int value) { for (int elt : arr)
    { if (elt == value)
        { return true;
        }
    } return false;
}

但是,其实在这里,我们不关心,元素在数组中的位置,所以我们可以使用 for-each 简化代码,代码如下:

for (int value : arr)
{
    System.out.println(value);
}
2
3
1

字符串的比较

需要的注意是,查找元素是否在数组中,涉及到数组元素的比较。如果是原始类型,比如 int,double,boolean,char 类型,我们是可以直接用==来比较两个元素是否相等;但是对于 String 以及其他的对象类型,我们知道 == 实际上比较的是,两个对象在内存对地址是否一致。
对于字符串来说,== 比较就是两个字符串在内存地址;所以比较字符串或者对象是否相等,我们需要用字符串的 equals 方法。
所以查找字符串是否在字符串数组中的方法,下面写法是错误的:

字符串比较 == 比较内存地址

public static boolean have(String[] arr, String value) { for (String elt : arr)
    { if (elt == value) // 注意比较内容是否一致,不能用 == 要用 equals 方法 { return true;
        }
    } return false;
}

比较字符串的内容是否相等应该用 equals 方法,正确代码如下:

euqals 比较内容

public static boolean haveEquals(String[] arr, String value) { for (String elt : arr)
    { if (elt.equals(value)) // 注意比较内容是否一致,不能用 == 要用 equals 方法 { return true;
        }
    } return false;
}
String[] list = {new String("a"), "b", "c"};
have(list, "a")
false 

字符串字面量

因为new String("a")是用运算符 new 创建的,也就是要在内存中分配新的内存空间;所以这个字符串对象和字符串字面量 "a" 在内存中的位置是不同的,所以尽管都是 a,也找不到。

String[] list = {"a", "b", "c"};
have(list, "a")
true 

因为 "a" 是字符串字面量,内容相同的字符串字面量在 Java 是保存在内存的同一位置的,所以用 have 可以找到。

String[] list = {new String("a"), "b", "c"};
haveEquals(list, "a");
true 

因为字符串字面量 "a" 和字符对象 new String("a"),尽管在内存中保存地址不同,但是内容是相同的,都是 a,所以可以正确的的找到。要注意对象类型的比较,equals 才是比较是否是相同的类型。

自定 equals 方法比较对象

Point 类比较

class Point { private int x, y; public Point(int x, int y) { this.x = x; this.y = y;
    } public int getX() {return x;} public int getY() {return y;} public boolean equals(Point other) { if (other == null)
        { return false;
        } return x == other.getX() && y == other.getY();
    }
}
Point p231 = new Point(2, 3);
Point p232 = new Point(2, 3);
System.out.println(p231 + " " + p232)
REPL.$JShell$55$Point@74517d53 REPL.$JShell$55$Point@7a969da9

通过代码的运行结果,观察 @ 之后的内容,发现两个对象其实是在不同的地址。

Point p = p231;

System.out.println(p231 + " " + p)

REPL.$JShell$55$Point@74517d53 REPL.$JShell$55$Point@74517d53

通过观察地址,我们可以知道 p 就是对象 p221 的 alias name,也就是别名,所以用 == 比较结果肯定是 true; 因为我们知道,p 也好,p231 也好本质上是保存了对待在内存中的地址,而不是对象本身。

p == p231
true 
p231 == p232
false 
p231.equals(p232)
true 

对于对象类来说,两个对象比较,判断是否相等或者是相同对象的逻辑是可以自己定义的,就像是 toString 是可以自己定义。我们可以规定,什么样的两个对象是相等的。对于两个长方形,如果左上角的顶点相同,并且长和宽也相同,我们就可以认为这是两个相同的长方形。

自定义 equals 的比较逻辑

class Rect { private Point point; private int w, h; public Rect(Point p, int w, int h) { this.w = w; this.h = h; this.point = p;
    } public Point getPoint() {return point;} public int getW() {return w;} public int getH() {return h;} public boolean equals(Rect other) { if (other == null) {return false;} // return point == other.getPoint() && w == other.getW() && h == other.getH(); return point.equals(other.getPoint()) && w == other.getW() && h == other.getH();
    } public String toString() { return point.getX() + " " + point.getY() + " " + w + " " + h;
    }
}
Rect r1 = new Rect(new Point(2, 3), 2, 3);
Rect r2 = new Rect(new Point(2, 3), 2, 3);
r1 == r2 // 两个对象在内存中的地址不同 
false 
r1.equals(r2) // 相等的逻辑是我们自己定义的,只要左上角坐标和长款一致,就是相同的长方形 
true 
r1
2 3 2 3
r2
2 3 2 3

语雀导出的 markdown 可以包含换行和 wrap,对于代码来说排版尤为重要,否则普通的无法导入到公众号。 2023 对自己好点,但行好事,莫问前程。

上一篇

除了牛津、剑桥外最难申请的英国大学是哪所?

下一篇

Financial Aid是什么?Financial Aid有哪些种类?

你也可能喜欢

评论已经被关闭。

插入图片
老师微信 老师微信
老师微信
返回顶部