怎样实现快速定位数据—–让数据有规则的存储;
规定好每个数据存的字节数,这样去找时,只要能找到规律就可以不用将全部数据取出来遍历;
由于我们要规定好每个变量存储的字节数,所以不可以用oblectOutputStream,这个会一次性将对象存进去,但是无法做到精确规定,所以可以用DataOutputStream来存数据,做到把每一个变量所占的空间都精确规定;
写数据时会出现的问题:
写名称时,由于不确定到底名称会占几个字节,所以可以给20个字节的长度来存放;
但是DataOutputStream对于写字符串提供了writeUTF()的方法,可我们要写的是GBK,因为GBK规定了汉字占两个字节,这样比较有规律;所以我们使用write(byte[])将字符串转为byte[]即可;可是转成的byte[]不一定是20个字节,所以我们需要new一个20个字节的数组出来,并且将上个数组的数据拷贝到新数组中,可以使用jdk提供的数组拷贝方式:
System.arraycopy(bytes, 0, b, 0, bytes.length);
括号内第一个参数是被拷贝的原数组,第二个是从原数组下标第几个开始拷贝,第三个是需要拷贝到哪个数组,第四个是目标数组中放数据的起始位置,第五个是拷贝的长度;
存放数据代码如下:
public class SaveData {
public static void main(String[] args) throws Exception {
Prodcut p0 = new Prodcut(0, "苹果", 10.5f);
Prodcut p1 = new Prodcut(1, "泰国榴莲", 16.5f);
Prodcut p2 = new Prodcut(2, "菠萝", 20.5f);
Prodcut p3 = new Prodcut(3, "菠萝蜜", 30.5f);
Prodcut p4 = new Prodcut(4, "香蕉", 19.5f);
save(p0);
save(p1);
save(p2);
save(p3);
save(p4);
}
public static void save(Prodcut p) throws Exception {
// 将数据按照既定的规则存入文件中
// id占4个字节,name占20字节,price占4个字节
DataOutputStream dout = new DataOutputStream(new FileOutputStream("d:/p.dat",true));
dout.writeInt(p.getId()); //写入id
byte[] bytes = p.getName().getBytes("GBK");
byte[] b = new byte[20];
// jdk提供的数组拷贝工具方法: 参数1:源数据数组 参数2:从源数组的第几个位置开始拷贝
// 参数3 :目标数组 参数4:目标数组中放数据的起始位置 参数5:拷贝的长度
System.arraycopy(bytes, 0, b, 0, bytes.length);
dout.write(b); // 将商品名写入文件,占20个字节
dout.writeFloat(p.getPrice()); //将价格按照float数写入文件,占4个字节
dout.close();
}
}
读取时的问题,由于用来20个字节存名字,会导致读取后有空格,用trim()去掉空格;
并且要快速定位就不能从头读起了,而是要从某个字节开始读,所以要用RandomAccessFile,
创建方式如下:RandomAccessFile raf = new RandomAccessFile(“d:/p.dat”, “r”);
括号里是文件路径和读取方式,“r”是只读的意思;然后用raf.seek(96)就是跳的96开始读;具体代码如下:
public static Product readDataById(String path,int id) throws Exception{
RandomAccessFile raf = new RandomAccessFile(path, “r”);
//指定查找位置
long pos=id*28;
File file = new File(path);
long length = file.length();
if(pos<length){
raf.seek(pos);
int pId = raf.readInt();
// 再读20个字节
byte[] b = new byte[20];
int read = raf.read(b);
// 然后将这20个字节转成字符串,但是尾部有大量空格
String string = new String(b);
// 去掉首尾的空格
String name = string.trim();
// 再读价格
float price = raf.readFloat();
raf.close();
Product prodcut = new Product(pId, name, price);
return prodcut;
}else {
return null;
}
}
public static ArrayList<Product> readDataByName(String path,String name) throws Exception{
RandomAccessFile raf = new RandomAccessFile(path, "r");
//创建一个list;
ArrayList<Product> proList = new ArrayList<>();
File file = new File(path);
long length = file.length();
byte[] b = new byte[20];
//指定查找位置
for(int i=0;i<length/28;i++){
raf.seek(4+28*i);
raf.read(b);
String str = new String(b);
if(str.contains(name)){
String proName = str.trim();
// 再读价格
float proPrice = raf.readFloat();
int proId = i;
Product pro = new Product(proId, proName, proPrice);
proList.add(pro);
}
}
return proList;
}
public static ArrayList<Product> readDataByPrice(String path,String price) throws Exception{
RandomAccessFile raf = new RandomAccessFile(path, "r");
//创建一个list;
ArrayList<Product> proList = new ArrayList<>();
//分割传进来的字符串
String[] split = price.split("~");
int minPrice =Integer.parseInt(split[0]);
int maxPrice =Integer.parseInt(split[1]);
//判断文件的长度
File file = new File(path);
long length = file.length();
byte[] b = new byte[20];
//指定查找位置
for(int i=0;i<length/28;i++){
raf.seek(24+28*i);
float proPrice=raf.readFloat();
if(proPrice>=minPrice&&proPrice<=maxPrice){
// 再读价格
int proId = i;
raf.seek(4+28*i);
int read = raf.read(b);
// 然后将这20个字节转成字符串,但是尾部有大量空格
String string = new String(b);
// 去掉首尾的空格
String proName = string.trim();
Product pro = new Product(proId, proName, proPrice);
proList.add(pro);
}
}
return proList;
}