菜鸟学习笔记:Java容器4——Collections工具类、其他容器
- Collections工具类
- 容器其他知识点
- 队列Queue
- Enumeration接口
- Hashtable
- Properties
- Map接口其他容器
- 引用分类
- 枚举类
- WeakHashMap
- IdentityHashMap
- EnumMap
- 同步控制与只读设置
Collections工具类
Java对我们平时常用操作进行了封装,构成了java.util.Collections工具类,我们对其中的几个常用方法进行讲解。
- **binarySearch(List<? extends Comparable<? super T>> list, T key)**二分查找,在List容器中查找key。底层用二分法实现。
- sort(List list) 排序,可通过修改容器存储类型的compareTo方法修改排序规则。
- sort(List list, Comparator<? super T> c) 排序,通过修改Comparator接口中的compare方法来修改排序规则。
- reverse(List<?> list) 容器反转。将容器中所有元素对调,比如数组[1,2,3,4,5]会变成[5,4,3,2,1]。
- shuffle(List<?> list) 洗牌,将List中的元素打乱。
- swap(List<?> list, int i, int j) 将指定容器的两个索引位置的数据进行交换。
我们借一个斗地主发牌功能的实现来讲解一下collections类的用法:
public static void main(String[] args) {
List<Integer> cards =new ArrayList<Integer>();
//shuffle 洗牌 模拟斗地主
for(int i=0;i<54;i++){
cards.add(i);
}
//洗牌
Collections.shuffle(cards) ;
//依次发牌
List<Integer> p1 =new ArrayList<Integer>();
List<Integer> p2 =new ArrayList<Integer>();
List<Integer> p3 =new ArrayList<Integer>();
List<Integer> last =new ArrayList<Integer>();
for(int i=0;i<51;i+=3){
p1.add(cards.get(i));
p2.add(cards.get(i+1));
p3.add(cards.get(i+2));
}
//最后三张为底牌
last.add(cards.get(51));
last.add(cards.get(52));
last.add(cards.get(53));
System.out.println("第一个人:"+p1);
System.out.println("第二个人:"+p2);
System.out.println("第三个人:"+p3);
System.out.println("底牌为:"+last);
}
容器其他知识点
这里会补充讲述一些不太常用的容器,但很多是在面试题中的考点,大家有必要了解一下。
队列Queue
队列是一种常用的数据结构,它的特点就是先进先出,List的子接口Queue实现了队列效果,演示代码如下:
//银行排队取款案例
public class Demo01 {
/**
* @param args
*/
public static void main(String[] args) {
//新建队列
Queue<Request> que =new ArrayDeque<Request>();
//模拟排队情况
for(int i=0;i<10;i++){
final int num =i;
//入队采用offer方法
que.offer(new Request(){
@Override
public void deposit() {
System.out.println("第"+num+"个人,办理存款业务,存款额度为:"+(Math.random()*10000));
}
});
}
dealWith(que);//会按照顺序打印从第1个人到第10个人的存款额度
}
//处理业务
public static void dealWith(Queue<Request> que){
Request req =null;
//出队采用poll方法
while(null!=(req=que.poll())){
req.deposit();
}
}
}
interface Request{
//存款
void deposit();
}
Enumeration接口
用法与之前讲的Iterator类似,有两个方法。
- hasMoreElements与hasNext对应
- next方法与nextElement方法对应
记得容器关系图中有一个Vector容器,它与ArrayList类似,不同点在于它是线程安全的,而且它实现了Enumeration接口。所以我们把Enumeration和Vector结合起来举例:
public static void main(String[] args) {
Vector<String> vector =new Vector<String>();
vector.add("javase");
vector.add("html");
vector.add("oracle");
//遍历该Vector
Enumeration<String> en =vector.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
}
此外Enumeration接口有一个实现类叫做StringTokenizer,它可以用于字符串分割,但它不支持正则表达式,所以这个类了解即可,不必太过深入。
Hashtable
Hashtable也是Map的实现类,它的功能和HashMap相类似,区别在于它继承与Directory类,而HashMap继承AbstractMap类,还有就是Hashtable线程安全的,并且它的键与值均不能为null。(面试考点)
Properties
Hashtable有一个实现类Properties,这个类很重要需要大家掌握,因为今后的项目中许多像Mysql数据库连接等的配置是写在.Properties配置文件中的,Java读取.Properties文件就是通过Properties类来实现。
Properties类要求键与值只能为字符串,常用方法:
容器读写
- **setProperty(String key,String value)**给容器中设置元素
- **getProperty(String key)**返回key对应值,没有返回null
- **getProperty(String key, String defaultValue)**返回key对应值,没有返回defaultValue
将容器内容读出为文件:
读出为.Properties配置文件:
- store(OutputStream out, String comments)
- store(Writer writer, String comments)
- load(InputStream inStream)
- load(Reader reader)
OutputStream和Writer,InputStream和Reader是我们之后要讲的字节流和字符流,大家先留个印象,学完就知道了, comments代表我们要加入的注释。
读出为.xml文件:
- storeToXML(OutputStream os, String comment)
- storeToXML(OutputStream os, String comment, String encoding)
- loadFromXML(InputStream inStream)
第一个方法指定默认字符集为UTF-8,第二个方法可以指定输出的字符集。
在讲解案例之前先先了解一下绝对路径和相对路径的问题。
绝对路径指的是以电脑根路径开始的存储位置比如"D:\Program\db.properties"。
相对路径指的是以当前工程项目的更目录开始计算的路径,比如"src/db.properties"表示当前项目下的src目录下的db.properties。
下面通过数据库配置文件读写的案例来说明下Properties的使用。
写出配置文件:
public static void main(String[] args) throws FileNotFoundException, IOException {
//创建对象
Properties pro =new Properties();
//存储
pro.setProperty("driver", "oracle.jdbc.driver.OracleDriver");
pro.setProperty("url", "jdbc:oracle:thin:@localhost:1521:orcl");
pro.setProperty("user", "scott");
pro.setProperty("pwd", "tiger");
//存储到e:/others 绝对路径 盘符:
//pro.store(new FileOutputStream(new File("e:/others/db.properties")), "db配置");
//pro.storeToXML(new FileOutputStream(new File("e:/others/db.xml")), "db配置");
//使用相对路径 会保存到以当前的工程为根的路径下
// pro.store(new FileOutputStream(new File("db.properties")), "db配置");
// pro.store(new FileOutputStream(new File("src/db.properties")), "db配置");
pro.store(new FileOutputStream(new File("src/db.properties")), "db配置");
}
读取配置文件
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties pro=new Properties();
//读取 绝对路径
//pro.load(new FileReader("e:/others/db.properties"));
//读取 相对路径
pro.load(new FileReader("src/db.properties"));
System.out.println(pro.getProperty("user", "Not Found"));
//输出scott
}
这里涉及了许多IO流相关的操作与异常,我们在讲完容器后在深入讨论。
最后再介绍两个获取类相对路径的方法
public static void main(String[] args) throws IOException {
Properties pro =new Properties();
//二者均表示类所在的根路径加上传入的字符串
pro.load(Demo04.class.getResourceAsStream("/db.properties"));//src/db.properties
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));//src/db.properties
}
Map接口其他容器
引用分类
要讲引用分类会涉及到垃圾回收机制对引用的控制,涉及到jvm相关知识,虽然很重要有点超出了初学者的范围,所以这里不作为重点来讲解,大家了解即可。
- 强引用:引用指向的对象,垃圾回收器运行时不回收。(我们到现在学过的所有引用都是强引用)
- 软引用:当JVM内存不够时垃圾回收器运行时会回收。
- 弱引用:垃圾回收器运行时立即回收。
- 虚引用:类似于无引用,主要跟踪对象被回收得状态,不能单独使用,必须与引用队列联合使用。
枚举类
依据之前的知识,我们可以通过public static final来定义常量,但这样会带来一下的缺陷:
- 类型不安全。若一个方法中要求传入季节这个参数,用常量的话,形参就是int类型,开发者传入任意类型的int类型值就行。
- 没有命名空间。开发者要在命名的时候以SEASON_开头,这样另外一个开发者再看这段代码的时候,才知道这四个常量分别代表季节。
为此Java中引入了枚举这个特殊的类,可以用于存放常量,定义方式如下:
public enum SeasonEnum {
SPRING,SUMMER,FALL,WINTER;
}
其中SPRING,SUMMER,FALL,WINTER代表四个常量。可以为其写入常量代表值,当然枚举类中同样可以加入属性和方法:
public enum SeasonEnum {
SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天");
private final String name;
private SeasonEnum(String name)
{
this.name = name;
}
public String getName() {
return name;
}
}
使用时可以直接通过SeasonEnum.SPRING来获得该常量。
WeakHashMap
键为弱引用,回收键后自动删除key-value对象。
public static void main(String[] args) {
WeakHashMap<String,String> map =new WeakHashMap<String,String>();
//测试数据
//常量池对象,不会回收
map.put("abc", "a");
map.put("d", "test");
//gc运行 已被回收
map.put(new String("bjsxt"), "c");
map.put(new String("dsf"), "d");
//通知回收
System.gc();
System.runFinalization();
System.out.println(map.size());//2
}
IdentityHashMap
键只以地址去重,而不是比较hashcode和equals。意思就是无论你存得对象是什么,只要不是同一对象,那不管键内容是什么都不相同。
public static void main(String[] args) {
IdentityHashMap<String ,String> map =new IdentityHashMap<String,String>();
//常量池中的"a",地址相同
map.put("a", "a1");
map.put("a", "a2");
System.out.println(map.size());
//两个对象地址不同
map.put(new String("a"), "a3");
map.put(new String("a"), "a4");
System.out.println(map.size());
}
EnumMap
键要求必须是枚举的值。
public class EnumMapDemo {
public static void main(String[] args) {
EnumMap<Season,String> map =new EnumMap<Season,String>(Season.class);
//存放值
map.put(Season.SPRING, "春困");
map.put(Season.SUMMER, "夏无力");
map.put(Season.AUTUMN, "秋乏");
map.put(Season.WINTER, "冬眠");
System.out.println(map.size());
}
}
//季节
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
同步控制与只读设置
同步控制表示多线程并发访问集合的线程安全(这里做了解,多线程问题以后讲)。
由于常用容器ArrayList、HashSet、HashMap等都是线程不安全的所以Collections提供了syhchronizedXxx()方法,将指定容器包装成同步。
举例说明:
public static void main(String[] args) {
List<String> list =new ArrayList<String>();
list.add("a");
list.add("b");
//list可以同步
List<String> synList =Collections.synchronizedList(list);
}
只读设置代表将容器设置为不可改变的,可以通过Collections提供的三种方法实现:
- emptyXxx()空的不可变的集合。
- sinqletonXxx()只有一个元素且不可变的集合。
- unmodifiableXxx()不可变容器。
public static void main(String[] args) {
Map<String,String> map =new HashMap<String,String>();
map.put("test", "test");
map.put("aaa", "aaa");
//只读控制
Map<String,String> map2 =Collections.unmodifiableMap(map);
//map2.put("a", "a"); //不能操作
System.out.println(map2.size());
//一个元素的容器测试
List<String> list =Collections.singletonList(new String());
list.add("test");
//list.add("ccc"); //只能包含一个元素的容器
}
. 至此容器的内容就全部讲完了,对于容器运用不难,只要熟悉api就行,重要的是理解它的内部实现,所以建议大家有时间认真阅读源码,在熟悉原理的基础上才能更好的运用。