一:题目--文章需要解决的问题
题目的意思也可以表述为:
将一堆项集数据,根据数据的第一列值是否相同,来判断第二列的值是否该进行合并,相当于使之变成序列数据集。
题目的样例1表述:
输出的数据格式 (可以先用Excel进行升序处理,便于userID相等的呆在一起)
a,v1
a,v2
b,v3
a,v4
c,v3
c,v5
数据预处理后后的结果:
a,v1|v2|v4
b,v3
c,v3|v5
题目的样例2表述:
userid value
0 1
0 3
0 5
1 2
1 4
数据预处理后的结果:
userid value
0 1 3 5
1 2 4
二:题目解决的思路
主要判断当前行的userid是否与上一行的userid相等,若相等,则进行添加,使之成为一行即成为一个单独的序列;若不想等,则新起一行,成为一个新的序列的开头数据。
借助list,hashmap,map的value,key等Java的数据结构进行数据操作;借助stringBuffer,StringBuilder,BufferedWriter,BufferReader等进行文件相关数据读取,写入等操作;利用泛型方法进行数据的拼接,利用迭代器进行集合的遍历。
三:具体代码及注释
package temp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class mosaicFile2 {
public static void WriteCSV2(File csv, StringBuffer sBuffer) {
BufferedWriter bw;
/*
* BufferedWriter类:用于加快写入的速度。
* 构造方法: BufferedWriter bf = new BufferedWriter(Write out)
* 主要方法: void write(): 写入单个字符
* void newLine(): 写入一个行分隔符
* void close(): 关闭该流
*
* */
try {
bw = new BufferedWriter(new FileWriter(csv, true));
// 添加新的数据行
bw.write(new String(sBuffer));
bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
} // 附加
}
public static void ReadCSV2(FileReader fr){
try {
//key,values
BufferedReader reader = new BufferedReader(fr);
/*
* bufferedReader类:为了提高读的效率而设计的一个包装类,它可以包装字符流;可以从字符输入流中读取文本,
* 缓冲各个字符,从而实现字符,数组和行的高效读取
* 构造方法:BufferedReader br = new BufferedReader(reader in)
* 常用方法:int read() :读取单个字符
* String readLine(): 读取一个文本行
* void close():关闭该流,并释放与该流相关的所有资源
*/
///reader.readLine();//第一行信息,为标题信息,不用,如果需要,注释掉
reader.readLine(); //因为我们的csv文件的第一列有列属性(即列名称)
String line = null;
String flag="-1"; //作为一个标志,即上一行的第一列数值的值(
//没有后续判断当前行与上一行是否相等的作用,只起到启动第一行的作用)
StringBuffer all=new StringBuffer();
/*
* StringBuffer :
* 构造方法:StringBuffer a = new StringBuffer()
* 初始化方法:StringBuffer a = new StringnnnnBuffer(“java”);
* 1)是一个string的缓冲区,和string不同的是,它可以被修改。StringBuffer在任意时刻都有
* 一个任意的字符串序列。
* 2)StringBuffer继承了抽象类AbstractStringBuilder,它本质上是一个字符数组。
* 常用方法:append(String str): 把任意类型添加到字符串缓冲区里面,并且返回字符串缓冲区本身
* insert(int offset, String str): 插入
* delete(index): 删除指定位置的字符
*/
String lastUserId=""; //初始化为空
int count=0;
// Map<String, List<String>> map1=new HashMap<>();
Map<String, String> map=new HashMap<>();
/* Map
* 1)java自带了各种map类.HashMap是最常用的map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,
* 具有很快的访问速度
* 2)Map是个接口,HashMap是它的实现类
* 3)Map的初始化: Map<String,String> map = new HashMap<String,String>();
* 插入元素 map.put("key1","value1")
* 获取元素 map.get("key1")
* 移除元素 map.remove("key1")
* 清空map map.clear()
*
*/
List<String> list=new ArrayList<>();
/*
* List ArrayList
* 1)List集合是一个容器,可以添加各种各样的数据.
* 2)使用此接口能够精确地控制每个元素的插入位置。用户能够使用索引(索引即元素爱list中的位置,类似于数组的下标)
* 3)方法: void add(String item) // 依次往后添加元素
* void add(String item, int index) //在指定位置处添加元素
* void remove(int position) //删除第几个元素,索引从0开始
* list.clear():删除列表中所有元素
*/
System.out.println("输出通过list处理后的数据:");
while((line=reader.readLine())!=null){
String item[] = line.split(",");//CSV格式文件为逗号分隔符文件,这里根据逗号切分
// 把该行的数据分成两列,第一列放在item[0]里面;第二列放在item[1]里面
if(item!=null){
//System.out.println("yewei"+item[0]+" "+item[1]);
if (flag=="-1") { //最开始flag已经被初始化为1了。
list.add(item[1]);
//lastUser=item[0];
flag="-2";
lastUserId=item[0];
}
else {
if (lastUserId.equals(item[0])&&list.size()>=1) {
// 判断当前这一行的userid与上一行的userid是否相等,当前的list的长度是否大于1
// 只有当上面两个条件都满足时候,才会把第二列的数据进行添加
list.add(item[1]);
}else {
if (!lastUserId.equals(item[0])) {
// 当上一行的userid与当前行的userid不相等时候,说明当前行是一个新用户
System.out.println(lastUserId+" "+list);//一行一行地处理
//别忘了list输出里面的数据会自动有[]和逗号呀,和数组一样
map.put(lastUserId, toString(list, ' '));
/* 调用后面定义的toString的这个泛型,即把list里面的元素放进map里面,并且元素
之间使用符号"|"隔开.
注意:这里面为什么要使用map.put呢?这是便于我们在运行对话框里面可以直接输出,
即在内存中可以直接输出,其实与写文件进行保存结果的关系不大,这将由下一行代码完成。
*/
//all.append(lastUserId+","+toString(list, ' '));
all.append(toString(list, ' '));
//我们在这儿把第一列的同一userid和它对应的第二列的数据添加到all这个StringBuffer
//WriteCSV2(new File("result01.csv"),all);
//WriteCSV2(new File("result01.txt"),all);
//WriteCSV2(new File("res114.csv"),all);
//WriteCSV2(new File("res114.txt"),all);
WriteCSV2(new File("finalresult.csv"),all);
WriteCSV2(new File("finalresult.txt"),all);
// 把all里面的数据写入到指定的文件里面,依旧是scv格式
all.setLength(0);//进行清空,便于后面可以继续使用,避免了多次创建StringBuffer的all。
//map.put(string, list);
lastUserId=item[0]; //更新lastUserId,即把当前行的lastUserId赋值给上一个。
list.clear(); //释放,因为新一个的UserID来临
//System.out.println(list.size());
count++; //统计有多少个不同的userID
list.add(item[1]); //新出现的一个userId添加到list(此时list里面就一个元素喔)
}
}
}
}
}
System.out.println("此时已经完成写入文件操作,并且map里面也存储完数据");
System.out.println("接下来我们输出map里面暂时存储的数据");
for (Map.Entry<String, String> entry : map.entrySet()) {
/* Entry:
* map中采用entry内部类表示一个映射项。映射项包括key和value。一个entry就是一个键值对.
* map.entry里面包含getKey()和getValue()方法
* entrySet
* entrySet是键值对的集合,set的类型是map.entry,一般可以通过map.entrySet()得到
* map的遍历方法:
* for(Map.Entry<String,String> entry:map.entrySet())
* ***** 特别注意理解:map中一个键对应是一个键值,但是这个键值可以是string类型呀,因此在成为键值的前面
* 可以利用泛型方法进行第二列值的append,append结束后再放入map里面充当键值。
*/
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
System.out.println("输出map里面的键值对的数量 "+map.entrySet().size());
System.out.println( "统计出userId的数量:"+"count:="+count);
} catch (Exception e) {
e.printStackTrace();
//System.out.println("File Complete!!");
}
}
public static <T> String toString(List<T> list,char ch) { //定义一个T类型,返回值是String类型。括号中为参数
/*
* Java泛型: 也被称为模版编程
* 本质上是参数化类型,也就是说所操作的数据类型被指定为一个参数。举例理解,假设有这样的一个需求,写一个
* 排序算法,能够对整型数组,字符串数组,甚至任何其他类型的数组进行排序--->这个时候就可以使用泛型,写
* 一个泛型方法来对对象数组排序,任何调用该泛型方法就可以对整型数组,祖父处数组排序来
* 泛型和多态的区别:
* 泛型编程是编译期替换;多态则是运行期作类型转换
* 泛型方法(代码是如何的?):
* 该方法在调用时,可以接收不同类型的参数;所有泛型方法都有一个类型参数声明部分,该类型参数声明部分在方法
* 返回类型之前(eg <t>在void/String之前)。 每一个类型参数声明部分包含一个或者多个类型参数,参数
* 间用逗号隔开,其中一个是泛型参数,也被称为一个类型参数eg:List<T> list
*/
Iterator<T> it = list.iterator();
/*
* java迭代器:
* 由于集合的内部结构不同,很多时候可能不知道该怎样去遍历一个集合中的元素,因此引入来迭代器模式。
* 迭代器的使用:
* 1)使用方法iterator() 要求集合返回一个iterator,iterator将准备好返回集合的第一个元素
* 2)使用next()获取集合的下一个元素
* 3)使用hasNext()检查集合中是否还存在元素(为boolean,存在会返回true)
* 4)使用remove()将迭代器新近返回的元素删除。
* 注意:如果迭代器的指针已经指向了集合的末尾,那么如果再调用next()会返回NoSuchElementException异常
* 如何定义(list)迭代器:
* Iterator<type> temp = list.iterator(); //获取list专属的迭代器
*/
if (! it.hasNext())
return ""; //如果集合是空的,则返回空;如果集合不是空的,则接着往下走,执行下面的语句
//StringBuilder sb = new StringBuilder();
StringBuffer sb = new StringBuffer();
/*
* StringBuilder:
* 声明方式:StringBuilder sb = new StringBuilder(); //无参数构造方法
* StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全)
*/
// sb.append('[');
for (;;) {
T e = it.next(); //获取集合中的下一个元素
//sb.append(e == this ? "(this Collection)" : e);
sb.append(e); //把获取的元素添加到sb这个StringBufrrer里面
if (! it.hasNext()) //检查集合中是否还存在元素
// return sb.append(']').toString();
return sb.toString(); //如果不存在则结束吧?
// sb.append('*').append(' ');
sb.append(ch); // 如果集合中还存在元素,则插入一个符号标志,符号标志就是ch对应的值。
}
}
public static void main(String[] args) throws Exception{
ReadCSV2(new FileReader("data02.csv")); //需要处理的数据
//ReadCSV2(new FileReader("writers.csv"));
// /Users/mac/Desktop/study/Programme/python/project202004/mooc/dataprocessing/data01.csv
//ReadCSV2(new FileReader("Users\\mac\\Desktop\\study\\Programme\\python\\project202004\\mooc\\dataprocessing\\data01.csv"));
}
}
四:总结
要学会利用Java的一些现成方法进行数据的预处理操作
五:参考
参考的博客
|