目录
开始前,我们来看一道题:
假设有这样一个需求,有一个文件,文件里包含了公司所有客户的年龄数据,现在需要统计每一个年龄段有多少人,以便后续开展活动,给定的主机硬件为单台主机,2core+2G,文件大小为6G,在不使用现有的容器的情况下,如何最高效解决这个问题?所谓容器包括map,set等
如果不限制硬件资源以及容器技术等,那么可能上来我们直接拆开,将单个文件拆为多个文件,最后合并处理,但,限制了之后,就需要思考怎么做了
OK,现在我们来开始想办法解决,因为内存只有2G,所以不能整个文件都取出来,也不能进行排序,毕竟排序的前提是数据都在内存里
我们再来看一下已知条件,文件里的是客户的年龄数据,人类的年龄数据,是一个确定的范围,就算是很长寿,那也是0-200之间对吧
那,我们创建一个长度为200的数组,索引位0代表0岁,数据0位的值,代表0岁的人数,以此类推,一行一行读取文件内容,每次将对应的位置数量追加,直到文件处理完成,时间复杂度为O(n)
什么是数组,所谓数组,就是有序的元素序列,当然,这个有序并非我们通俗意义上的大小
如果将有限个类型相同的变量的集合明明,那么,这个名称就是数组名,组成数组的各个变量称做数组的分量,也称为数组中的元素,用于区分数组中各个元素的数字编号称为下标
数组通常用Array表示,也称为线性表

1、数组是相同类型元素的集合,一定是相同类型,是int,那就都是int,不可能有int有string
2、数组中的元素是有先后顺序的,并且,他们在内存中也是按照这个先后顺序连续放在一起的
3、数组的元素用整个数组的名字加上自己在数组中的位置来表示,比如第一个元素,那就是arr[0]
数组在表现形式上,分为一维数组和多维数组,一维数组就是最常用的,比如 int a[],而多维数组,相信大部分人也用过int a[][],如果没用过的话,想象一下矩阵
数组的结构决定了它增删慢,查找快的特点
比如我要在某个位置增加或删除一个数据,那这个变更位点的后面位置全都得变化,所以比较慢
而正是因为这个特点,数组的查找快,比如我要访问第五个数组,我直接访问第五个位置就好
让我们来实现一下数组的常用方法,也就是增删改查
- public class Test {
-
- private int[] arr;
- private int index;
-
- public Test(int size) {
- this.index = 0;
- this.arr = new int[size];
- }
-
- public void printArg() {
- for (int i = 0; i < arr.length; i++) {
- System.out.println(arr[i]);
- }
- }
-
- public int findArg(int index) {
- return arr[index];
- }
-
- public void update(int index, int to) {
- arr[index] = to;
- }
-
- public void insert(int index, int to) {
- int length = arr.length;
- int endIndex = index;
- if (endIndex++ < length) {
- for (int i = length - 1; i > index; i--) {
- arr[i] = arr[i - 1];
- }
- arr[index] = to;
- this.index++;
- } else {
- throw new RuntimeException("can't insert,index is out of arr's size");
- }
- }
-
- public void delete(int index) {
- int length = arr.length;
- for (int i = index; i < length; i++) {
- if (i != length - 1) {
- arr[i] = arr[i + 1];
- } else {
- arr[i] = 0;
- }
- }
- this.index--;
- }
-
- }
当然,这儿只是简单写了写,没有做扩容
数组的扩容,本质上还是重新申请地址,然后将旧数组的数据全都放到新数组里
说到这儿,Java中的array list,本质也是数组,但是对比数组,大家用的多的还是array list,因为array list是jdk帮我们封装好的,已经实现了自动扩容等等的操作,可以节省我们很多功夫,如果直接用数组,相关的方法肯定是需要我们自己去实现的
在应用中,一定要注意,小心越界
Java中内存分为两种,堆内存和栈内存,都是Java用来存放数据的地方
new出来的对象和数组都在堆内存
引用对象存放在栈内存
堆栈的区别:
举个例子
- String str1 = "abc";
- String str2 = new String ("abc");
- System.out.println(str1 == str2);
这是个false,因为str2是new出来的,而==对于引用类型来说,对比的是地址值
再来一个容易错的地方
- String str1 = "ja";
- String str2 = "va";
- String str3 = "java";
- String str4 = str1 + str2;
- System.out.println(str3 == str4);
这个的答案也是false,不要看起来值相同
问题出在这个加号上 ➕
Java重载了加号,在底层调用了stringbuilder,而stringbuilder是创建新对象
内存和数组也有关系,比如,数组为什么从0开始
假设创建了一个数组,申请了内存空间,是1001,1002,1003,.......
arr[0] 就应该存储在1001
arr[1]就应该存储在1002
对应的,结合下标,可以写成
arr[0] -> 1001 -> 1001+0
arr[1] -> 1002 -> 1001+1
这样,拿到第一个地址,就可以拿到后续所有地址
如果不从0开始,假设从1开始
arr[1] -> 1001 -> 1001+(1-1)
arr[1] -> 1002 -> 1001+(2-1)
从性能上来说,这种减号运算性能更差
数组的内存地址,可以用如这样表达
初始内存地址 + 数组下标*数据的长度
数组是一个最基础最简单的数据结构,存储的相同的数据类型
用数组的时候,记得关注下标,下标是数组最优的一个特点
数组增删慢,查找快