菜鸟学习笔记:Java提升篇4(容器4——Collections工具类、其他容器)

23 篇文章 0 订阅
23 篇文章 1 订阅

Collections工具类

Java对我们平时常用操作进行了封装,构成了java.util.Collections工具类,我们对其中的几个常用方法进行讲解。

  1. **binarySearch(List<? extends Comparable<? super T>> list, T key)**二分查找,在List容器中查找key。底层用二分法实现。
  2. sort(List list) 排序,可通过修改容器存储类型的compareTo方法修改排序规则。
  3. sort(List list, Comparator<? super T> c) 排序,通过修改Comparator接口中的compare方法来修改排序规则。
  4. reverse(List<?> list) 容器反转。将容器中所有元素对调,比如数组[1,2,3,4,5]会变成[5,4,3,2,1]。
  5. shuffle(List<?> list) 洗牌,将List中的元素打乱。
  6. 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来定义常量,但这样会带来一下的缺陷:

  1. 类型不安全。若一个方法中要求传入季节这个参数,用常量的话,形参就是int类型,开发者传入任意类型的int类型值就行。
  2. 没有命名空间。开发者要在命名的时候以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提供的三种方法实现:

  1. emptyXxx()空的不可变的集合。
  2. sinqletonXxx()只有一个元素且不可变的集合。
  3. 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就行,重要的是理解它的内部实现,所以建议大家有时间认真阅读源码,在熟悉原理的基础上才能更好的运用。
上一篇:菜鸟学习笔记:Java提升篇3(容器3——泛型、排序)
下一篇:菜鸟学习笔记:Java提升篇5(IO流1——IO流的概念、字节流、字符流、缓冲流、转换流)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值