剩余任务:1,常见的集合类总结,底层原理和常见的API ;2,自定义一个正则表达式,从指定字符串中查找满足条件的内容并输出;3,比较String,StringBuffer,StringBuild的区别,有哪些常见的操作API;4,JDBC程序
1,List,Set都是继承Collection接口,都是用来存储一组相同类型的元素;
特点:List:元素有序放入顺序,元素可重复
Set:元素无放入顺序,元素不可重复,所以有时候可以使用Set来去重,在插入的时候,set在元素插入时是要有一定的方法来判断是否重复
List主要有ArrayList,LinkList,Vector这些实现
ArrayList本质就是数组,使用set和get方法来进行访问的,由于是数组的数据结构,所以在数据中部新增数据或者删除数据的话,底层就会复制原来的数据到新开辟的内存中,再将原来的数据删除,这就影响到性能,
但是查询数据会很快;
而LinkList是一个双链表,在添加和删除数据方面是优于ArrayList的,但是由于是链表结构,在查询上,每次都需要从头结点开始,从而影响查询的速度
但是,ArrayList在设计好数组的大小,并采用尾插法的话,性能是很有可能优于LinkList;
Vector和ArrayList类似,但属于强同步类。如果线程本身就是线程安全的话,那么使用ArrayLIst是更好的选择;两者在更多元素添加进来会请求更大的空间。Vector每次请求其大小的双倍空间,而ArrayList每次对size增长50%;
LiskList还实现了Queue接口,该接口比List提供了更多的方法,包括offer(),peek(),poll()等;
还有一点,Vector是多线程环境下更为可靠的数据结构,所有方法都实现同步;
List可添加null;
使用List
来实现存在效率非常低的问题,而Map
这种键值(key-value)映射表的数据结构,作用就是能高效通过key
快速查找value
(元素)!
Set是如何保证元素的不重复?
Set实现方式分为HashSet和TreeSet,而HashSet底层是HashMap实现的,HashMap在添加值的时候,会计算其HashCode值,从而找到对的位置;
TreeSet底层是TreeMap的keySet(),而TreeMap是基于红黑树实现的,红黑树是一种平衡二叉树查找数,是按key排序的,所有元素在插入时,TreeSet会用compareTo()判断重复元素的
Properties:
在编写应用程序的时候,经常需要读写配置文件。例如,用户的设置:
# 上次最后打开的文件:
last_open_file=/data/hello.txt
# 自动保存文件的时间间隔:
auto_save_interval=60
配置文件的特点是,它的Key-Value一般都是String
-String
类型的,因此我们完全可以用Map<String, String>
来表示它。
因为配置文件非常常用,所以Java集合库提供了一个Properties
来表示一组“配置”。由于历史遗留原因,Properties
内部本质上是一个Hashtable
,但我们只需要用到Properties
自身关于读写配置的接口。
Queue:
队列(Queue
)是一种经常使用的集合。Queue
实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List
的区别在于,List
可以在任意位置添加和删除元素,而Queue
只有两个操作:
- 把元素添加到队列末尾;
- 从队列头部取出元素。
在Java的标准库中,队列接口Queue
定义了以下几个方法:
-
-
int size()
:获取队列长度; -
boolean add(E)
/boolean offer(E)
:添加元素到队尾; -
E remove()
/E poll()
:获取队首元素并从队列中删除; -
E element()
/E peek()
:获取队首元素但并不从队列中删除; - 对于具体的实现类,有的Queue有最大队列长度限制,有的Queue没有。注意到添加、删除和获取队列元素总是有两个方法,这是因为在添加或获取元素失败时,这两个方法的行为是不同的。我们用一个表格总结如下:
-
throw Exception | 返回false或null | |
---|---|---|
添加元素到队尾 | add(E e) | boolean offer(E e) |
取队首元素并删除 | E remove() | E poll() |
取队首元素但不删除 | E element() | E peek() |
使用Stack
栈(Stack)是一种后进先出(LIFO:Last In First Out)的数据结构。所谓FIFO,是最先进队列的元素一定最早出队列,而LIFO是最后进Stack
的元素一定最早出Stack
。如何做到这一点呢?只需要把队列的一端封死:
因此,Stack
是这样一种数据结构:只能不断地往Stack
中压入(push)元素,最后进去的必须最早弹出(pop)来
Stack
只有入栈和出栈的操作:
- 把元素压栈:
push(E)
; - 把栈顶的元素“弹出”:
pop()
; - 取栈顶元素但不弹出:
peek()
。
在Java中,我们用Deque
可以实现Stack
的功能:
- 把元素压栈:
push(E)
/addFirst(E)
; - 把栈顶的元素“弹出”:
pop()
/removeFirst()
; - 取栈顶元素但不弹出:
peek()
/peekFirst()
。
为什么Java的集合类没有单独的Stack
接口呢?因为有个遗留类名字就叫Stack
,出于兼容性考虑,所以没办法创建Stack
接口,只能用Deque
接口来“模拟”一个Stack
了。
当我们把Deque
作为Stack
使用时,注意只调用push()
/pop()
/peek()
方法,不要调用addFirst()
/removeFirst()
/peekFirst()
方法,这样代码更加清晰。
Iterator:
Java的集合类都可以使用for each
循环,List
、Set
和Queue
会迭代每个元素,Map
会迭代每个key。
2,自定义一个正则表达式,从指定字符串中查找满足条件的内容并输出
在软件开发中,正则表达式是个很有用的功能,使用正则表达式可以简化代码,省去不少时间。
下面记录一个正则表达式的用法,就是找出一堆乱码中,找出以1开头的电话号码,并写入一个文件中
废话不多说,直接上代码:
12.txt里面是一堆乱码,其中也含有一堆数字,就可能有电话号码。在网络数据爬取的时候,就需要正则表达式来提取有用的信息,就比如特定格式的电话号码。
Parrern对象获取规则,用Matcher对象里的方法find()去查找可能的值,被存储到Mather里的group()方法中。
3,比较String,StringBuffer,StringBuild的区别
String是final修饰的,是不可变的,每次操作都会产生新对象,而StringBuffer、StringBuilder都是在原对象上操作
StringBuffer是线程安全的,StringBuilder是线程不安全的,因为StringBuffer都是synchronized修饰的
性能:StringBuilder > StringBuffer > String
参考博客:https://blog.csdn.net/itchuxuezhe_yang/article/details/89966303
String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
这里使用10000次对String、StringBuilder和StringBuffer对象进行修改操作,计算完成时间并打出来,结果可以看出,Sring的完成时间最长,StringBuilder和StringBuffer的执行时间差不多。
4,JDBC程序
JDBC是Java DataBase Connectivity的缩写,它是Java程序访问数据库的标准接口。
使用Java程序访问数据库时,Java代码并不是直接通过TCP连接去访问数据库,而是通过JDBC接口来访问,而JDBC接口则通过JDBC驱动来实现真正对数据库的访问。
例如,我们在Java代码中如果要访问MySQL,那么必须编写代码操作JDBC接口。注意到JDBC接口是Java标准库自带的,所以可以直接编译。而具体的JDBC驱动是由数据库厂商提供的,例如,MySQL的JDBC驱动由Oracle提供。因此,访问某个具体的数据库,我们只需要引入该厂商提供的JDBC驱动,就可以通过JDBC接口来访问,这样保证了Java程序编写的是一套数据库访问代码,却可以访问各种不同的数据库,因为他们都提供了标准的JDBC驱动:
Java App ————>JDBC Interface(JDK)————>JDBC Driver(Vendor)————>Database
JDBC接口的Connection
代表一个JDBC连接;
使用JDBC查询时,总是使用PreparedStatement
进行查询而不是Statement
;
查询结果总是ResultSet
,即使使用聚合查询也不例外。
连接数据库几个参数值得注意,其他的没啥了
用最新的驱动(Driver),以及getConnection中url地址一定得填对,后面就是账号密码
获取连接后,我们就可以写sql语句来操作数据库了
这个用增加和修改的例子来演示
我们在讲多线程的时候说过,创建线程是一个昂贵的操作,如果有大量的小任务需要执行,并且频繁地创建和销毁线程,实际上会消耗大量的系统资源,往往创建和消耗线程所耗费的时间比执行任务的时间还长,所以,为了提高效率,可以用线程池。
类似的,在执行JDBC的增删改查的操作时,如果每一次操作都来一次打开连接,操作,关闭连接,那么创建和销毁JDBC连接的开销就太大了。为了避免频繁地创建和销毁JDBC连接,我们可以通过连接池(Connection Pool)复用已经创建好的连接。
这里就不演示了。