JavaSE学习笔记-Day15

一. Hashtable

Hashtable类和HashMap用法几乎一样,底层实现几乎一样,只不过Hashtable的方法添加了synchronized关键字确保线程同步检查,效率较低。

HashMap与Hashtable的区别:

  1. HashMap: 线程不安全,效率高。允许key或value为null
  2. Hashtable: 线程安全,效率低。不允许key或value为null

二. Set接口

Set接口继承自Collection,Set接口中没有新增方法,方法和Collection保持完全一致。我们在前面通过List学习的方法,在Set中仍然适用。 Set常用的实现类有:HashSet、TreeSet等。Set接口的实现类中目前好像没有发现线程安全的。
在这里插入图片描述

Set容器特点:无序、不可重复

什么是无序?什么是不可重复?用代码体现更直观:

package cn.zjb.test;

import java.util.*;

/**
 * 测试Set接口的使用
 * @author 张坚波
 *
 */
public class TestSet {
	public static void main(String[] args) {
		
		Set<String> s1=new HashSet<>();
		s1.add("a");
		s1.add("b");
		s1.add("c");
		s1.add("c"); //重复值
		System.out.println(s1);	
		Set<String> s2=new HashSet<>();
		s2.add("c");
		s2.add("b");
		s2.add("a");
		s2.add("c"); //重复值
		System.out.println(s2);
		
		List<String> a1=new ArrayList<>();
		a1.add("a");
		a1.add("b");
		a1.add("c");
		a1.add("c"); //重复值
		System.out.println(a1);
		List<String> a2=new ArrayList<>();
		a2.add("c");
		a2.add("b");
		a2.add("a");
		a2.add("c"); //重复值
		System.out.println(a2);
		
	}
}

运行结果如下:
在这里插入图片描述

Set为什么会有无序性、不可重复这两个特点呢?不急,我们分析一下Set某个实现类的源码就好了。

三. HashSet

HashSet是采用哈希算法实现,底层代码为:
在这里插入图片描述
我们惊奇的发现,这哪是HashSet啊,这分明就是HashMap呀!还是简化版的HashMap,存储中有用的只有Key值,Value值是被固定下来的,为了节省内存定义了final Object PRESENT对象,所有的键值对的值都是它。还有,在构造器里发现,只要我们new了HashSet对象,那么系统就会直接帮你new好了HashMap对象。总结一句话:HashSet本质就是一个简化版的HashMap。

好了,我们还是来写个自己的HashSet吧:

package cn.zjb.practice;

/**
 * MyHashSet类是用来对数据进行存储和管理的容器类,存储数据的主体结构为哈希表。
 * <p>该类没有实现任何接口,并且是Object的直接子类。
 * @author 张坚波
 *
 */
public class MyHashSet<K> {
	
	/**
	 * 实现一个简易版HashMap。
	 */
	private MyHashMap<K,Object> map;
	
	/**
	 * 所有键值对共有的对象。
	 */
	private static final Object DEFAULT_INSTANCE=new Object();
	
	public MyHashSet() {
		map=new MyHashMap();
	}
	
	public void add(K key) {
		map.put(key, DEFAULT_INSTANCE );
	}
	
	public void remove(K key) {
		map.remove(key);
	}
	
	@Override
	public String toString() {
		String initial=map.toString();
		char[] result=new char[initial.length()];
		int length=0;
		boolean judge=false;
		for(int i=0;i<initial.length();i++) {
			if(initial.charAt(i)==':'||judge) {
				judge=true;
				if(initial.charAt(i)==','||initial.charAt(i)==']') {
					result[length++]=initial.charAt(i);
					judge=false;
					continue;
				}else
					continue;
			}else
				result[length++]=initial.charAt(i);	
		}
		return new String(result);
	}
	/*还可以考虑用栈实现:一开始一直进栈,
	*遇见','或者']'就开始出栈(这个先搁一边,从符号的前一个开始,最后补上),
	*直至是':'为止(':'也出栈)。
	*这可能才是正统思想吧,嘻嘻*/
	
	/*
	 * 以下省略,没必要实现了。
	 */
	
	
	
}
package cn.zjb.practice;
/**
 * practice包中主方法所在的类
 * @author 张坚波
 *
 */
public class MainClass {
	public static void main(String[] args) {
		
		MyHashSet<String> a=new MyHashSet<>();
		for(int i=0;i<10;i++) 
			a.add("zhang"+i);
		System.out.println(a);
		a.remove("zhang0");
		System.out.println(a);
	
	}
}

运行结果如下:
在这里插入图片描述

自己写HashSet的唯一难点就是toString()的方法上,因为输出值和HashMap的不同(并且HashMap的属性都是私有的),所以必须要手动处理字符串,这的确需要耗费一定的时间。那有没有什么好办法遍历容器呢?

我们发现:在HashSet、ArrayList、HashMap等等实现类中都没有重写toString()方法。但是我们知道,没有重写toString()方法在打印对象时一定是地址值,然而它们都能打印出元素,那么它们一定是有重写了的toString()方法,只不过不是它们自己的,可能是daddy类或者爷爷类、祖宗类的,它们继承了这个方法!

我就找啊找啊,终于找到了:
在这里插入图片描述

绿色是接口,红色是抽象类,黑色是实现类。上面这张图不重要。

AbstractCollection和AbstractMap里找到了toString()方法:
在这里插入图片描述
在这里插入图片描述
要想搞懂这个程序,我们还得学学这个Iterator。

四. Iterator迭代器

在Java内部,都是使用Iterator迭代器遍历容器元素(List/Set/Map),迭代器为我们提供了统一的遍历容器的方式。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
获得Iterator迭代器对象的方式是调用iterator()方法!这样一来,上面源码的两个程序也就能看懂了。

hasNext()和next()在不同数据结构中的实现方法是不一样的,所以Iterator只是一个规范,具体过程还是要根据具体的数据结构来实现。

我们不妨来练练手:

  1. 遍历List
package cn.zjb.test;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * 用Iterator迭代器遍历List。
 * @author 张坚波
 *
 */
public class TestIteratorOfList {
	public static void main(String[] args) {
		
		ArrayList<String> list=new ArrayList<>();
		for(int i=0;i<5;i++)
			list.add(""+i);
		//内部实现
		System.out.println(list); //[0, 1, 2, 3, 4]
		//自己实现
		for(Iterator<String> it=list.iterator();it.hasNext();)
			System.out.print(it.next()+" ");  //0 1 2 3 4 
		System.out.println('\n');
		
			
		LinkedList<String> list2=new LinkedList<>();
		for(int i=0;i<5;i++)
			list2.add(""+i);
		//内部实现
		System.out.println(list2); //[0, 1, 2, 3, 4]
		//自己实现
		for(Iterator<String> it=list2.iterator();it.hasNext();)
			System.out.print(it.next()+" "); //0 1 2 3 4 
		
	}
}
  1. 遍历Set
package cn.zjb.test;

import java.util.HashSet;
import java.util.Iterator;


/**
 * 用Iterator迭代器遍历Set。
 * @author 张坚波
 *
 */
public class TestIteratorOfSet {
	public static void main(String[] args) {
		
		HashSet<String> set=new HashSet<>();
		for(int i=0;i<5;i++)
			set.add(""+i);
		//内部实现
		System.out.println(set); //[0, 1, 2, 3, 4]
		//自己实现
		for(Iterator<String> it=set.iterator();it.hasNext();)
			System.out.print(it.next()+" ");  //0 1 2 3 4 
		System.out.println('\n');
		
	}
}
  1. 遍历Map
package cn.zjb.test;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 用Iterator迭代器遍历Set。
 * @author 张坚波
 *
 */
public class TestIteratorOfMap {
	public static void main(String[] args) {
		
		HashMap<String,String> map=new HashMap<>();
		for(int i=0;i<5;i++)
			map.put("zhang"+i,""+i);
		//内部实现
		System.out.println(map); 
		
		/* ------------------------自己实现-------------------------*/
		
		/*
		 * 第一种方式:
		 * 将Map转化为Set遍历,
		 * 即将键值对作为整体直接变成Key。
		 * 最后在键值对中利用getKey()、getValue()方法获取具体值。
		 */
		Set<Entry<String,String>> s=map.entrySet();
		for(Iterator<Entry<String,String>> it=s.iterator();it.hasNext();) {
			Entry<String,String> temp=it.next();
			System.out.println(temp.getKey()+": "+temp.getValue());
		}
		
		/*
		 * 第二种方式:
		 * 也是将Map转化为Set遍历,
		 * 和第一种不同的是,先舍弃Value值,直接将Key变成Set,
		 * 最后根据Key找Value。
		 * 个人感觉这种比较好。
		 */
		Set<String> s1=map.keySet();
		for(Iterator<String> it=s1.iterator();it.hasNext();) {
			String temp=it.next();
			System.out.println(temp+": "+map.get(temp));
		}
			
	}
}

总结:

  1. List和Set的遍历写法完全一致,
  2. Map的遍历需要靠entrySet()或者keySet()转化成Set。
  3. 先学会用,底层代码还不急,因为不用Iterator迭代器我们也能靠传统方法遍历。

五. Collections工具类

类 java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助静态方法

  1. void sort(List) :对List容器内的元素排序,排序的规则是按照升序进行排序。
  2. void shuffle(List) :对List容器内的元素进行随机排列。
  3. void reverse(List) :对List容器内的元素进行逆续排列 。
  4. void fill(List, Object) :用一个特定的对象覆盖原来整个List容器的内容。
  5. int binarySearch(List, Object):对于顺序的List容器,采用折半查找的方法查找特定对象。
  6. 等等
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值