正文
3、常见的集合及方法
在日常的数据分析工作中,常常需要使用到集合来存储和处理数据,因此需要大家对集合的分类和功能有所了解。Java的集合框架分为两部分,分别对应两大接口:Collection接口和Map接口。以下就通过这两大接口开始讲解。
1.Collection接口
Collection接口涉及三种类型的集合:1.Set(规则集) 2.List(线性表) 3.Queue(队列),其层级关系如图:
ps:图片源自网络
这三种类型集合的常用方法及特性总结如下:
2.Map接口
Map接口涉及三种类型的集合:1.HashMap 2.LinkedHashMap 3.TreeMap。其层级关系如下:
Map----
|
|----SortMap----TreeMap
|
|----HashMap----LinkedHashMap
Map的特性为键值不能重复。每个键值对应着一个值,键与值一起存储在集合中。
Map接口中有如下方法:
clear() //删除所有条目 containsKey(Object key) //如果包含指定键值返回true containsValue(Object value) //如果包含指定值返回true get(Object key) //获得指定键值对应的值 entrySet() //返回包含条目的规则集 isEmpty() //判断是否空 keySet() //返回包含键值的一个规则集 put(Object key, Object value) //添加键值对 putAll( ) //将指定实例中的键值对添加到当前实例中 remove(Object key) //删除指定键值对应的值 size() //键值对个数 values() //返回包含的集合
4、常用的字符串处理方法
数据分析工作中,最基本的一项工作就是通过hive写类sql语言处理数据,而类sql语法中处理字符串的方法都是通过对java的字符串处理方法进行一层封装得到的,接下来,我们就一起来看下常用Java字符串处理方法有哪些。
字符串查找
String提供了两种查找字符串的方法,即indexOf与lastIndexOf方法。
1、indexOf(String s)
该方法用于返回参数字符串s在指定字符串中首次出现的索引位置,如果没有检索到字符串s,该方法返回-1
String str ="We are students"; int size = str.indexOf("a"); // 变量size的值是3
2、lastIndexOf(String str)
该方法用于返回字符串最后一次出现的索引位置。如果没有检索到字符串str,该方法返回-1。如果lastIndexOf方法中的参数是空字符串"" ,则返回的结果与length方法的返回结果相同。
获取指定索引位置的字符
使用charAt()方法可将指定索引处的字符返回。
String str = "hello word"; char mychar = str.charAt(5); // mychar的结果是w
获取子字符串
通过String类的substring()方法可对字符串进行截取。
1、substring(int beginIndex)
该方法返回的是从指定的索引位置开始截取直到该字符串结尾的子串。
String str = "Hello,word"; String substr = str.substring(3); //获取字符串,此时substr值为lo,word
2、substring(int beginIndex, int endIndex)
String str = "Hello word"; String substr = str.substring(0,3); //substr的值为hel
去除空格
trim()方法返回字符串的副本,忽略前导空格和尾部空格。
字符串替换
replace()方法可实现将指定的字符或字符串替换成新的字符或字符串
String str= "address"; String newstr = str.replace("a","A");// newstr的值为Address
判断字符串的开始与结尾
startsWith()方法与endsWith()方法分别用于判断字符串是否以指定的内容开始或结束。这两个方法的返回值都为boolean类型。
1、startsWith(Stringprefix)
该方法用于判断当前字符串对象的前缀是否是参数指定的字符串。
2、endsWith(Stringsuffix)
该方法用于判断当前字符串是否以给定的子字符串结束
判断字符串是否相等
1、equals(Stringotherstr)
如果两个字符串具有相同的字符和长度,则使用equals()方法比较时,返回true。同时equals()方法比较时区分大小写。
2、equalsIgnoreCase(Stringotherstr)
equalsIgnoreCase()方法与equals()类似,不过在比较时忽略了大小写。
字母大小写转换
字符串的toLowerCase()方法可将字符串中的所有字符从大写字母改写为小写字母,而toUpperCase()方法可将字符串中的小写字母改写为大写字母。
str.toLowerCase();
str.toUpperCase();
字符串分割
使用split()方法可以使字符串按指定的分隔字符或字符串对内容进行分割,并将分割后的结果存放在字符串数组中。
str.split('&');
str.split(String sign, in limit);
该方法可根据给定的分割符对字符串进行拆分,并限定拆分的次数。
5、常用的日期处理方法
另一个在数据分析工作中,跟字符串处理一样使用较为频繁的就是关于日期的相关处理。
其中最常用到的日期处理类有:java.util.Date、java.util.Calendar、java.text.SimpleDateFormat。
1.java.util.Date的使用
构造函数
Date() :分配 Date 对象并用当前时间初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date) :分配 Date 对象并初始化此对象,以表示自从标准基准时间(即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
常用方法
2.java.text.SimpleDateFormat的使用
java.text.SimpleDateFormat主要用于格式化日期,需要说明的一点是该类的实例是线程不安全的。
构造函数
public SimpleDateFormat(String pattern):pattern是描述日期和时间格式的模式 如:yyyyMMDD
使用方式
public class TestDateFormat { public voiddateFormat(){ Date date= new Date(); //创建不同的日期格式 SimpleDateFormat dt1 = new SimpleDateFormat('YYYY-MM-DD HH:MM:SS'); dt1.format(date);//返回的日期格式如:2019-05-13 04:03:45 SimpleDateFormat dt2 = new SimpleDateFormat('YYYY-MM-DD'); dt2.format(date);//返回的日期格式如:2019-05-13 SimpleDateFormat df3 = new SimpleDateFormat("yyyy年MM月dd日 hh时mm分ss秒 EE",Locale.CHINA) ; dt3.format(date);//返回的日期格式如:2019年05月13日 04时03分45秒 星期一 } }
3.java.util.Calendar的使用
java.util.Calendar是个抽象类,它可以通过特定的方式设置和读取日期的特定部分,比如年、月、日、时等,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
创建实例
Calendar dt = Calendar.getInstance();
常见操作
//设置时间 dt.setTime(new Date()); //获取年月日时分秒 dt.get(Calendar.YEAR); dt.get(Calendar.MONTH); dt.get(Calendar.DAY_OF_MONTH); dt.get(Calendar.HOUR); dt.get(Calendar.MINUTE); dt.get(Calendar.SECOND); //获取上午下午 dt.get(Calendar.AM_PM); //获取一周中的星期几 dt.get(Calendar.DAY_OF_WEEK); //当前日期基础上加减指定天数 dt.add(Calendar.YEAR, -1); dt.sub(Calendar.YEAR, -1);
6、json的解析与操作
json的表达能力非常强,一方面拥有常用的数据类型,另一方面可以表达复杂的数据结构。因此,在大数据领域,经常使用json作为信息的载体,将数据封装起来。所以,理解json的结构,对json进行解析与操作,在数据分析工作中非常重要。
下面是几个常用的 JSON 解析类库:
Json官方:Douglas Crockford在2001年开始推广使用的数据格式,解析最具有通用性,但是有点小麻烦
Gson:谷歌开发的 JSON 库,功能十分全面
FastJson:阿里巴巴开发的 JSON 库,性能十分优秀
Jackson:社区十分活跃且更新速度很快
下面我们主要通过代码示例来了解下java变量和json格式之间的相互转化以及json对象与字符串的相互转化。
1.编码
从 Java 变量到 JSON 格式的编码过程如下:
public void testJson() { JSONObject object = new JSONObject(); //string object.put("string","string"); //int object.put("int",2); //boolean object.put("boolean",true); //array List<Integer> integers = Arrays.asList(1,2,3); object.put("list",integers); //null object.put("null",null); System.out.println(object); }
输出结果如下:
{"boolean":true,"string":"string","list":[1,2,3],"int":2}
2.解码
从 JSON 对象到 Java 变量的解码过程如下:
public void testJson2() { JSONObjectobject = JSONObject .parseObject("{\"boolean\":true,\"string\":\"string\",\"list\":[1,2,3],\"int\":2}"); //string String s = object.getString("string"); System.out.println(s); //int int i = object.getIntValue("int"); System.out.println(i); //boolean boolean b = object.getBooleanValue("boolean"); System.out.println(b); //list List<Integer> integers = JSON.parseArray(object.getJSONArray("list").toJSONString(),Integer.class); integers.forEach(System.out::println); //null System.out.println(object.getString("null")); }
打印结果如下
string2 true 1 2 3 null
3.JSON 对象与字符串的相互转化
//前提说明:JSON是一个抽象类,以下实例中的parseObject(String text)、parseArray(String text)、toJSONString(JSONObject obj)、toJSONString(JSONArray arr)是该抽象类的静态方法 //从字符串解析JSON对象 JSONObject obj =JSON.parseObject("{\"runoob\":\"测试实例\"}"); //从字符串解析JSON数组 JSONArray arr =JSON.parseArray("[\"测试实例\",\"RUNOOB\"]\n"); //将JSON对象转化为字符串 String objStr =JSON.toJSONString(obj); //将JSON数组转化为字符串 String arrStr =JSON.toJSONString(arr);
7、正则表达式
数据分析过程中,经常需要对字符串进行匹配、替换、提取等操作,而有时简单的字符串处理方法已经无法满足复杂的处理逻辑时,就需要使用到正则表达式来完成。
这部分的内容,在ETL中的数据提取和处理环节应用非常频繁,所以大家要重点掌握。
概念
正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。
--摘自百度百科
作用
1.匹配:给定的字符串是否符合正则表达式的过滤逻辑
2.提取:可以将匹配成功的特定部分提取出来
3.替换:针对匹配成功的特定部分,替换为新的字符串
规则
图片摘自云游道士的博客
PS:由于篇幅有限,上述规则只是列出了常用的一些规则,如果在实际工作中发现上述规则不满足业务需求,可以百度一下,寻找其他规则。以下为一些常用的匹配示例。
示例
注意事项
1. 使用"|"时,应尽量使用括号来标识边界,否则容易引起歧义
2. 当使用正则匹配是否包含某一字符串时,不需要在前后加模糊匹配,否则会引起性能下降。例如匹配文本(“This is a pretty dog”)中是否包含“pre”开头的单词时,正则表达式应写作“pre[a-z]+”,而不要写作“.*pre[a-z]+.*”
3. 所有的特殊字符在[ ]内都将失去其原有的特殊含义,使用时要特别注意。上面的示例中密码的匹配就是一个字符(+?)含义变化例子
4. 正则表达式中的符号都是英文半角符号
5. 写好正则后,尤其是复杂的表达式,最好可以测试一下。
可以使用在线工具完成测试,如:
https://c.runoob.com/front-end/854
http://tool.oschina.net/regex/
在java中的使用示例
import java.util.regex.*; // 下面只是使用示例代码,不能直接运行 // 目标字符串 String url ="https://aa.bb.com/cn/index.html"; // URL匹配规则 String regex = "^(https?://)?[\S]+$"; // 编译正则表达式 Pattern pattern =Pattern.compile(regex); // 匹配正则表达式与目标字符串,返回匹配结果对象 Matcher matcher =pattern.matcher(url); // 判断字符串是否与正则表达式匹配成功 boolean result =matcher.matches(); System.out.println(result);
PS:在java中还有一种忽略正则表达式内英文字母大小写的写法,意思是在匹配时,忽略目标字符串中英文字母的大小写,都能匹配成功,写法如下所示:
// 忽略大小写的写法 Pattern pattern =Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
8、异常处理
相信大家对异常都不陌生,当程序运行中发生错误时,通常都会抛出异常(Exception)来警醒开发人员,如果不处理就会导致程序直接退出。
数据分析工作中,经常会遇到各种问题,这时异常便提供了找到相应问题诱发原因的重要线索,因此我们要保持良好的编码习惯,避免错过重要提示线索。
讲解这部分知识的主要目的,是用于数据分析日常工作中排查程序问题。怎么捕获异常,怎么根据异常寻找错误发生的位置,相信大家都比较熟悉了,这里我们不再赘述,而是说一下异常处理常遇到的几种问题。
1.你可能没捕获到你的“异常”
我们常见的异常都是Exception的子类,但是异常的顶级父类是Throwable,它下面有两个子类一个是Exception,另一个是Error。由于Error非常少见,所以大家通常会忽略它。如果你发现你的try catch语句没能捕获住“异常”,那就要考虑下,抛出的可能是Error。
还有一种可能性,就是分布式程序的异常。例如,编写Spark的处理代码时,要能分清你的代码是运行在Driver还是Executor上,如果异常发生在Executor,而你在Driver上捕获,那么也会出现无法捕获的问题。
2.捕获异常后,不要让异常被吃掉
工作中经常被问到一个问题,我的代码没有任何报错,但是运行后却没有输出结果,或者结果数据量很少。
为什么会这样呢?
这是因为,有些同学在捕获异常时,可能不太关心异常的发生,如由于脏数据引起的异常(非法json解析失败),捕获到这种解析异常,只需要过滤掉对应脏数据即可,便在catch语句块中什么都没写。
这虽不影响程序运行,但是却吞掉了非常重要的信息。尤其是使用异常的父类Exception来进行捕获时,可能有超出你预想的异常,就这样被吃掉了。
根据实际工作的经验,建议大家至少要把错误打印出来,方便后续排查。不然很容易出现,程序内部一直在出错,但是表面上却风平浪静。
打印异常有两种方式,一是只打印异常摘要信息,即打印异常的message,通过getMessage()方法获得后打印;
另一种是打印详细的堆栈信息,通过调用printStackTrace()方法完成详细信息打印。一般建议,在Executor中执行的代码(如逐条数据处理的代码)打印摘要信息,在Driver上执行的代码,打印堆栈信息。
3. 不要在finally里使用return
finally代码块通常是用来做一些必须要做的收尾工作,如释放资源、修改特定标记位等。
尽量不要在finally代码块中使用return,这会使代码的执行结果变得不好预期。如果需要确保一定有返回值,请catch住所有异常,然后在finally代码块的下面写return。
4. 巧妙利用异常返回信息
当调用一个方法时,通常我们会使用return给调用者返回数据。但还有一种不太常用的做法,就是使用异常返回信息,让调用者通过捕获异常的方式,获取到异常内携带的信息。关于这种方式的应用,在后续讲解spark时会提到。
5. 合理使用自定义异常
通过自定义异常,我们可以建立自己的异常处理体系,针对不同的业务错误,进行相应等级的处理。举个例子,在数据处理过程中,需要读取多个数据源,但是可能存在要读取的路径不存在,或者读入的数据量为0等情况,这时通常需要阻断流程,最好的方式就是抛异常,但有时我们又希望阻断部分流程,这时只有抛一个自定义的异常,然后外层流程控制中捕获这个异常,有针对性地处理才能达成目的。
9、JDBC
最后,关于java连接数据库的桥梁jdbc自然要提及一下,主要还是讲讲如何使用。
1.加载数据库驱动
//1.加载驱动Class.forName("com.mysql.jdbc.Driver");
2.建立连接
//URL用于标识数据库的位置,username和password是访问数据的用户名和密码 String url ="jdbc:mysql://localhost:3306/test"; String username ="test"; String password ="yqz56258"; Connection conn = null //2.获取与数据库的链接 conn =DriverManager.getConnection(url, username, password);
3.执行sql获得结果集
Statement st = null; //3.获取用于向数据库发送sql语句的statement st =conn.createStatement(); //4.向数据库发sql String sql = "select id,name,sex,age from teachers"; ResultSet rs =st.executeQuery(sql);
4.结果集遍历
//next():类似指针的效果,会向下移动 while(rs.next()){ //getString(int index):根据索引获取指定位置的值 String id = rs.getString(1); String name = rs.getString(2); String sex = rs.getString(3); //getString(String columnname):根据列名获取本列的值 String age =rs.getString("age"); }
10、小结
到此为止,所有在大数据工作中最常用到的java基础知识算是带着大家温习一遍啦,让我们一起来回顾下总共讲了哪些知识点:
基础概念和特性(面向对象、类、对象、封装、继承、多态、泛型)、常见的集合及方法、常用的字符串处理方法、常用的日期处理方法、json的解析与操作、正则表达式、异常处理、JDBC。
推荐阅读:https://blog.csdn.net/weixin_39032019/article/details/117997723
推荐阅读:https://blog.csdn.net/weixin_39032019/article/details/117737757