思考这样一道题目:
给你一个文件(近6G的文件)里面包含了全国人民(大约14亿)的年龄数据(大约是0~180左右),现在需要统计每个年龄段有多少人?
单机+2cpu+2G内存 以上情况你如何以最高效的的方法来解决这个问题?
用数组来解决: int[0] 这个下标0表示年龄 int[0] 的值是表示0岁的有多少人
String str = null;
String fileName = "D:\\age.txt"; //这个就是全部年龄数据文件
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName),"UTF-8");
long start = System.currentTimeMillis();
BufferedReader br = new BufferedReader(isr);
int tot = 0 ;
int data [] = new int[200];
while((str = br.readLine()) != null){ //一行一行的读 O(n)
int age = Integer.valueOf(str);
data[age] ++ ;
tot ++ ;
}
System.out.println("总共的数据大小: " + tot); //总共的数据大小: 1400000000
for(int i = 0 ; i < 200 ; i ++){//下标从0开始的
System.out.println(i + ":" + data[i]);
}
System.out.println("计算花费的时间为:" + (System.currentTimeMillis() - start) + "ms"); //计算花费的时间为:89170ms
1、数组的定义
数组是指有序的元素序列。如果将有限个类型相同的变量的集合命名,那么这个名称就是数组名,而组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组各个元素的数字编号称为下标。数组在程序设计中,为了处理方便,把具有相同类型的的若干元素组织起来的一种形式,这些无序排列的同类数据元素的集合称为数组。
2、数组的特点
1、数组是相同数据类型的元素集合。
2、数组当中的各个元素存储是有先后顺序的,他们在内存中按照这个先后顺序连续的存放在一起。
3、数组元素用整个数组的名字和它自己在数组中的顺序位置来表示。例如,a[0]表示名字为a的数组中的第一个元素,a[1]代表数组a的第二个元素,以此类推。
3、表现形式
1、一维数组
int a[], String a[]
2、多维数组
Int a,int a[]
4、随机访问
数组是连续的内存空间和相同类型的数据。正是因为这两个限制,它才有了一个非常重要的特性:随机访问,也就是查询是非常快速的且高效的,但有利就有弊,这两个限制也让数组的很多操作变得非常低效,比如要想在数组中删除、插入一个数据,为了保证连续性,就需要做大量的数据搬移工作。
这是简单的数组增删元素的内存示意图
这个是数组操作的简单代码实现 其实也是我们常用的ArrayList代码的简单实现,当然JDK封装的ArrayList的实现更复杂,涉及到扩容什么的。
public class ArrayTest {
private int arraySize; //数组长度
private int data[];
private int index; //当前已经存的数大小
/**
* 构造函数初始化
* @param arraySize
*/
public ArrayTest(int arraySize){
this.arraySize = arraySize;
data = new int[arraySize];
this.index = 0;
}
/**
* 简单打印
*/
public void print(){
System.out.println("index:"+index);
for (int i = 0; i <index ; i++) {
System.out.println(data[i]+" ");
}
System.out.println();
}
/**
* 数组添加元素
* @param loc
* @param n
*/
public void add(int loc,int n){ //时间复杂度 分析极端情况 从第一个元素插入 时间复杂度就是O(n) 最后一个元素插入时间复杂度就是O(1)
if(index++ < arraySize){
for (int i = arraySize-1; i > loc ; i--) {
data[i] = data[i-1];
}
data[loc] = n;
}
}
/**
* 数组删除元素
* @param loc
*/
public void delete(int loc){ //时间复杂度 分析极端情况 从第一个元素删除 时间复杂度就是O(n) 最后一个元素删除时间复杂度就是O(1)
for (int i = loc; i < arraySize ; i++) {
if(i!= arraySize-1){ //
data[i] = data[i+1];
}else{
data[i] = 0;
}
}
index--;
}
/**
* 数组更新元素
* @param loc
* @param n
*/
public void update(int loc,int n){ //时间复杂度 O(1)
data[loc] = n;
}
/**
* 数组的查询
* @param loc
* @return
*/
public int get(int loc){ //时间复杂度 O(1)
return data[loc];
}
}
ArrayList和数组:
其实他们本质是一样的,都是数组。
ArrayList是JDK封装的 提供了操作数组的api,包括扩容操作也实现了。
数组操作的话需要自己全部实现。
ArrayList和数组选择:
不知道数组大小的情况下选择ArrayList
如果知道大小且对性能要求很高的话,选择数组。
数组最需要注意的就是数组越界 ArrayIndexOutOfBoundsException
为什么很多计算机编程语言中的数组下标是从0开始的?
当我们定义数组的时候,根据数组的特点(连续的)在内存分配空间的时候,分配是一段连续的内存空间
如 int a = new int[3]
假如申请的内控地址是 10001 10002 10003
int[0] ==> 10001 ==> 10001+0*typesize
int[1] ==> 10002 ==> 10001+1*typesize
int[2] ==> 10003 ==> 10001+2*typesize
后面的 0 1 2就是对应前面数组的下标
假如我们不是从0开始 而是1开始
int[1] ==> 10001 ==> 10001+(1-1)*typesize
int[2] ==> 10002 ==> 10001+(2-1)*typesize
int[3] ==> 10003 ==> 10001+(3-1)*typesize
这样就多了一步的底层运算 而且了解计算机底层计算的话,减法运算其实就是加一个负数 ,其中会涉及到反码补码等操作。