Thinking in Java整理笔记

来源:互联网 发布:淘宝nike专卖店 编辑:程序博客网 时间:2024/06/05 19:18

Thinking in Java整理笔记

阅读时间:2017/4/16至2017/4/25
记录:吴义

2.1 第二题100亿整数文件的排序与文件归一
2.2 第三题找出两个文件中同时出现的URL并统计出现频率
2.3 OSI七层网络模型
第2章Thinking in Java各章节知识重点&遗漏点
2.1 对象导论
2.2 一切都是对象
2.3 操作符
2.4 控制执行流程
2.5 初始化与清理
2.6 访问控制权限
2.7 复用类
2.8 多态
2.9 接口
2.10 内部类
2.11 持有对象
2.12 通过异常处理错误
2.13 字符串
2.14 类型信息
2.15 泛型
2.16 数组
2.17 深入对象研究
2.18 Java I/O系统
2.19 枚举类型
2.20 注解
2.21 并发
2.22 图形化界面
第3章总结

1.1 第二题100亿整数文件的排序与文件归一
问题:有100个文件,每个文件中存放了100亿个从小大排序的整数,需要将这100个文件合并为一个文件,同时保证其中存储的整数仍旧为从小到大排列,应该怎么实现?
我的解答过程如下:
由于数目实在是太大,自己不好编写相应的测试文件,所以这里我用的是程序自动生成这一百组数据文件,分别以Data1.txt,Data2.txt,Data3.txt…….来进行命名,每组数据存储有1亿的数据量。
比如运行程序自动生成Data1.txt、Data2.txt……数据文件,一直是一百组。
生成这100个数据文件共100亿个测试数据的程序如下:

//将测试数据写入到本地电脑E盘,该函数最好只运行一次

public void writeTestData(){    for(int i=1;i<=100;i++) {        String fileName = "Data" + i + ".txt";        try {            PrintWriter pw=new PrintWriter(new FileWriter("E:\\TestData\\"+fileName));;            for(int j=i;j<100000000;j=j+100){                pw.println(j);            }            pw.close();        } catch (IOException e) {            e.printStackTrace();        }    }

这样,在E盘下的数据文件结构如下:

在这里,给出Data7.txt的数据结构,其他可以和它进行类比。每个txt文件里面含有一个亿的数据量。这部分生成的数据作为我们测试的数据用。下面将编写完成任务的程序,完成数据从小到大的排列且合并成一个文件的过程。大多数是在BufferedReader和PrintWriter和File这几个数据文件流进行操作的。所有测试代码如下:

package com.wuyi.data;import java.io.*;//"E:\\TestData\\Data1.txt"/** * Created by LENOVO on 2017/4/20. */public class Demo {    //将测试数据写入到本地电脑E盘,该函数最好只运行一次    public static void writeTestData(){        for(int i=1;i<=100;i++) {            String fileName = "Data" + i + ".txt";            try {                PrintWriter pw=new PrintWriter(new FileWriter("E:\\TestData\\"+fileName));;                for(int j=i;j<100000000;j=j+100){                    pw.println(j);                }                pw.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }    //返回txt文件的第一行    public static int getFirstData(String fileName){        try {            BufferedReader bf=new BufferedReader(new FileReader(fileName));            String firstLine=bf.readLine();            bf.close();            return Integer.parseInt(firstLine);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        catch (IOException e) {            e.printStackTrace();        }        return 0;    }    //txt文件删除第一行    public static void deleteFirstLine(String fileName){        int lineDel=1;        int linenum=0;        try {            BufferedReader bf=new BufferedReader(new FileReader(fileName));            PrintWriter pw=new PrintWriter(new FileWriter("E:\\TestData\\temp.txt"));            String line;            while((line=bf.readLine())!=null){                linenum++;                if(linenum==lineDel){                    continue;                }                pw.println(line);            }            bf.close();            pw.close();            File dataFile=new File(fileName);            dataFile.delete();            File newName=new File(fileName);            File f=new File("E:\\TestData\\temp.txt");            f.renameTo(newName);        } catch (FileNotFoundException e) {            e.printStackTrace();        }        catch (IOException e) {            e.printStackTrace();        }    }    //返回100个文件里第一个数值经过比较后的最小值,找到该最小的数据之后,重写入其他行里面去    public static int getMinFisrtNumInTotal100File(){        int minNum=getFirstData("E:\\TestData\\Data1.txt");//        System.out.println(minNum);        int firstData=0;        String targetFile="E:\\TestData\\Data1.txt";        for(int i=1;i<=100;i++){            String fileName = "Data" + i + ".txt";            firstData=getFirstData("E:\\TestData\\"+fileName);//            System.out.println("现在进入"+fileName+"查找,此时minNum的值是"+minNum+"firstData的值是"+firstData);            if(firstData<minNum){                minNum=firstData;                targetFile="E:\\TestData\\"+fileName;//                System.out.println(minNum);            }        }        System.out.println(targetFile);        deleteFirstLine(targetFile);        return minNum;    }    //进行数据和合并整理,但是此处注意,运行的时候会删除原来的数据文件,所以要提前对数据进行保留,否则造成数据的丢失    public static void conbineData(int length){        try {            PrintWriter pw=new PrintWriter(new FileWriter("E:\\TestData\\DataConbine.txt"));                for(int j=0;j<length;j++){                    int maxNum = getMinFisrtNumInTotal100File();                    pw.println(maxNum);                }            pw.flush();            pw.close();        } catch (IOException e) {            e.printStackTrace();        }    }    public static void main(String[] args) {        conbineData(200);    }}

由于不停的读取数据,且数据量巨大,写入读出数据等暂用我个人计算机的内存开销很大,所以程序运行一组大数据的读入读出最后经过比较再写入尤为耗费时间。比如,排序这100个文件按数字大小进行每排100个数据差不多得花费我个人计算机40s的时间,我最终选择把length设置为200,也就是排序200个数据就停止。如果排序设置为100亿的数据量,就算按照每100个数据花费0.5min,100亿个数据差不多要34722天,也就是我这台计算机要运行95年,程序算法肯定是没有经过优化的,我并没有写并发编程的算法(实际上是自己并不知道怎么写2333),且和计算级性能有关。运算后的排序如下:

可以看到,是按照要求进行排序的。
总而言之:
思维导图如下:
1.2 第三题找出两个文件中同时出现的URL并统计出现频率
2.给定a、b两个文件,各存放50亿个url,每个url各占64字节,内存限制是4G,如何统计同时存在于a、b文件中的url的数量?
答:按照题目来计算每个文件的大小应该是5.9个G,但是内存限制是4G,所以超过内存大小,我不知道该如何处理这种情况(是应该先把文件分解成多个片段

还是说我们一段一段这样进行分段处理,这样就可以保证不超过内存的限制??)我写的测试代码如下(数据量很小,只当一种解决问题的思路)
url1.txt和url2.txt两个文件分别保存了,
测试代码如下:
package com.wuyi.url;

import java.io.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* Created by LENOVO on 2017/4/25.
*/
public class CountFileUrl {

public static void main(String[] args) {    try {        BufferedReader bf1 = new BufferedReader(new FileReader("E:\\TestData\\UrlDictionary\\url1.txt"));        BufferedReader bf2 = new BufferedReader(new FileReader("E:\\TestData\\UrlDictionary\\url2.txt"));        String line;        Map <String, Integer> map1 = new HashMap <String, Integer>();        Map <String, Integer> map2 = new HashMap <String, Integer>();        Map <String, Integer> targetMap = new HashMap <String, Integer>();        while ((line = bf1.readLine()) != null) {            if (!map1.containsKey(line)) {                map1.put(line, 1);            } else {                Integer count = map1.get(line);                map1.put(line, ++count);            }        }        while ((line = bf2.readLine()) != null) {            if (!map2.containsKey(line)) {                map2.put(line, 1);            } else {                Integer count = map2.get(line);                map2.put(line, ++count);            }            if (map1.containsKey(line)) {                targetMap.put(line, (map1.get(line) + map2.get(line)));            }        }        System.out.println(map1);        System.out.println(map2);        System.out.println("结果如下:");        System.out.println(targetMap);    } catch (IOException e) {        e.printStackTrace();    }}

}
结果如下:
{www.sina.com=2, www.facebook.com=1, www.whu.edu.cn=1, www.youtube.com=1, www.baidu.com=1, www.souhu.com=1}
{www.hao123.com=1, www.sina.com=3, www.163.com=1, www.facebook.com=1, www.4399.com=1}
结果如下:
{www.sina.com=5, www.facebook.com=2}
1.3 OSI七层网络模型

第2章 Thinking in Java各章节知识整理笔记
2.1 对象导论
编程是对机器的模仿,是头脑的延伸。任何程序都是我们所设计系统的一种仿真。高内聚低耦合是程序设计要遵循的思想。
表1 类的成员访问修饰符的访问等级
访问修饰符 同一个类 同包 不同包,子类 不同包,非子类
private √
protected √ √ √
public √ √ √ √
默认 √ √
Java的动态绑定是默认的,不需要添加额外的关键字来实现多态
2.2 一切都是对象
Java程序运行时对象的储存,有如下几种
1)寄存器,其实在C语言里面就是加register关键字。是最块的存储区,因为它在处理器CPU的内部,数量有限,不能直接控制。
2)堆栈,位于RAM,堆栈指针下移,则重新分配内存,上移则释放内存,是一种快速有效的分配存储方法,仅次于寄存器。创建程序时,Java系统必须知道存储在堆栈内所有的确切生命周期,以便上下移动堆栈指针,JVM是基于堆栈的虚拟机.JVM为每个新创建的线程都分配一个堆栈.也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。
3)堆,用于存放所有的JAVA对象,总而言之就是,堆主要用来存放对象的,栈主要是用来执行程序的。堆中存的是new对象和数组。栈中存的是基本数据类型和堆中对象的引用,用堆进行储存的分配和清理可能比用堆栈进行存储分配需要更多的时间。关于堆和栈的具体关系请参考博客:
http://blog.csdn.net/HWHuangeian/article/details/49154243
4)静态存储分配,在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间.这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求.
永远不要销毁对象:当new一个对象时,只要我们需要,就会一直保留下去。Java有垃圾回收机制,用来监测new创建的所有对象,并分辨不会被引用的对象释放内存空间,也就是说,我们只关注创建对象,其他交给JVM。
Static关键字修饰的为类数据和类方法这有区别与实例方法。不需要new一个对象。
嵌入式文档:javadoc,如下:
/**
* Created by LENOVO on 2017/4/22.
*/
2.3 操作符
不需要去死记硬背那些操作符的优先级,不太清楚的加括号最保险。
知识遗漏点:在Random类中创建对象,通过创建Random对象提供种子,种子是用于随机数生成器的初始化值,随机数生成器对于特定的种子值总是产生相同的随机数序列,也就是我们可以在每一次执行程序时生成相同的随机数,因此输出是可以验证的。要是想生成不同的输出,需要更换种子数。
重点重写equals方法和hashCode方法:如String a=”abc”;是常量,对于常量之间的比较==和equals都是比较的值。而对于String a=new String(“abc”)将会是以对象的形式存在,对于任何非常量的比较==和equals()都是进行的对象引用地址(Java不能像C那样访问对象的真正地址,而只能访问引用地址)的比较,地址相同返回真否则假。对于重写equals()方法,在不用容器类的时候只需重写equals方法就可以了。比如下面:
class Student{
private int num;
private String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student){
Student stu = (Student) obj;
return (this.name.equals(stu.name)&&(this.num==stu.num));
}
return false;
}
public class EqualsDemo {
public static void main(String[] args) {
Student s1=new Student(1,”wuyi”);
Student s2=new Student(1,”wuyi”);
System.out.println(s1.equals(s2));//输出为true
}
}
返回的是true。但是要注意的是,一旦涉及到容器类Collection的时候,要比如HashMap等的添加的时,hashCode是用于散列数据的快速存取,如利用HashSet/HashMap/Hashtable类来存储数据时,都是根据存储对象的hashCode值来进行判断是否相同的。如果hashCode不同那么这个HashMap就断定这两者是不同的KEY,反之,当hashcode和equals都相同,这断定是同一个key,也就只添加一次,所以同时也必须根据内容重写hashcode方法来返回相同的hashcode(要注意的是,此时两个对象,如下面的s1和s2的内存地址仍旧是不相同的)
class Student{
private int num;
private String name;
public Student(int num, String name) {
this.num = num;
this.name = name;
}
@Override
public boolean equals(Object obj) {
if(obj instanceof Student){
Student stu = (Student) obj;
return (this.name.equals(stu.name)&&(this.num==stu.num));
}
return false;
}
@Override
public int hashCode() {
return (this.num+this.name.hashCode());
}
}
public class EqualsDemo {
public static void main(String[] args) {
Student s1=new Student(1,”wuyi”);
Student s2=new Student(1,”wuyi”);
HashMap