主要内容:
Map接口及实现类
Map接口
元素存储方式:
- Collection接口中的元素是单个存在的
- Map接口中的元素但是成对存储的,我们将这种存储数据的数据对,称之为键值对(key-value)
TreepMap和HashMap的用法是一模一样的,但是它是Tree,所以在进行比较的时候和上一篇文章的Set接口及实现类中有一个TreeSet的比较值得方法是一模一样的。
- 自然排序:都是通过实现Comparable接口,重写compareTo方法
- 定制排序:排序器类实现Comparetor接口,重写其中的compare方法
在这里就不做过多的冗余的介绍了。
那么我们就以HashMap为标准,学习Map接口的使用方式。
HashMap
上一篇文章讲了List和Map两个接口及实现类,今天要说的Map接口的实现类和前两个还是不一样的,Map集合使用的方法先看代码,代码如下:
public class TestHashMap {
public static void main(String[] args) {
//[1]创建Map接口实习类对象的构造方法
HashMap<String, Person> map1 = new HashMap<String, Person>();
//[2]增
map1.put("张三", new Person("张三", 22, "111111")); //向当前Map中单独添加键值对
map1.put("李四", new Person("李四", 23, "222222"));
map1.put("王五", new Person("王五", 20, "333333"));
map1.put("陈六", new Person("陈六", 21, "444444"));
map1.put("田七", new Person("田七", 24, "555555"));
HashMap<String, Person> map2 = new HashMap<String, Person>();
map2.putAll(map1); //将map1中所有的键值对批量添加到map2当中去
//测试Map中,键和值得重复性问题
//[1.1]测试:键重复,值不重复
Person p8 = new Person("王八", 26, "666666");
map1.put("张三", p8); //在Map中,键不能重复存储,如果键重复了,put方法将会变成修改方法
//[1.2]测试:键不重复,值重复
map1.put("白九", p8); //在Map集合中,同一个值是允许重复出现的
//[1.3]测试:键和值都重复
map1.put("白九", p8); //如果将一个重复的键值对存入Map中,键值对不会重复存储
/*
* 综上所述:
* 1.Map中的键是不能重复的(Set)
* 2.Map中的值是可以重复的(Collection)
*/
Person p3 = new Person("张三", 22, "111111");
map1.put("张三", p3);
map1.put("小张", p3);
//[3]删
Person rmv = map1.remove("白九"); //根据传入的key删除Map中的键值对,并肩这个键值对中的值进行返回
System.out.println("Remove by key: " + rmv);
boolean remove = map1.remove("张三", p8); //通过制定一组key和value删除Map中的一个键值对
System.out.println("Romove by key and value: " + remove);
// map1.clear(); //清空整个Map
//[4]改
/*
* Map集合中并没有提供独立的修改方法
* 在Map集合当中传递一个重复键的新值,
* 这个新的值将会替代这个键对应的原有的值
*/
Person rp = map1.replace("小张", p8); //按照指定的key将当前Map中的对应的value进行替换,被覆盖的值作为方法的返回值返回
System.out.println("Replace by key: " + rp);
boolean replace = map1.replace("张三", p8, p3); //只有在指定的key能够和oldValue对应的前提下,才能够将oldValue替换为newValue
System.out.println("Replace by key and value: " + replace);
/*
* [5]查:
* ①获取所有键构成的keySet,通过遍历keySet获取每个值,实现键值对遍历
* ②通过values()方法,直接获取所有value组成的Collection接口实现类对象,通过遍历这个集合实现对值的遍历
*/
System.out.println(map1);
//注意:在Map中因为值可能不是唯一的,所以不能通过值找到键
Person tmp = map1.get("小张"); //get方法是通过指定一个键,获取当前Map中与这个键对应的value值
System.out.println("Get by key: " + tmp);
//获取Map中所有键构成的集合——一个Set<K>的集合(本例中是Set<String>)
Set<String> keys = map1.keySet();
String tmpKey = "";
Person tmpValue = null;
Iterator<String> ite = keys.iterator();
while(ite.hasNext()) {
//[1]首先获取key的Set结合中的一个key(String)
tmpKey = ite.next(); //此时返回的是键的Set集合中,当前的一个key(String)
//[2]通过Map的get方法和上面获取的key来找到与之对应的value(Person)
tmpValue = map1.get(tmpKey);
//[3]打印分别获取的键和值
System.out.println("[key=" + tmpKey + ", value=" + tmpValue + "]");
}
System.out.println("----------");
//通过values()方法返回一个存储Map中所有值的一个Collection结合实现类对象,存储元素泛型为V(本例中V=Person)
Collection<Person> valueCollection = map1.values();
Iterator<Person> ite2 = valueCollection.iterator(); //获取包含所有值的迭代器对象
while(ite2.hasNext()) {
System.out.println("value=" + ite2.next());
}
System.out.println("----------");
//[6]其他方法
System.out.println("Size: " + map1.size()); //返回当前map中,键值对的对数
System.out.println("Is empty: " + map1.isEmpty()); //判断当前Map中是否不存在键值对
System.out.println("Contains key: " + map1.containsKey("李四")); //判断当前Map中是否包含指定的key
System.out.println("Contains value: " + map1.containsValue(p8)); //判断当前Map中是否包含指定的value
}
}
HashMap的内部实现原理
HashMap中对键值对的存储方式:
- 在Map当中,键和值不是分开存储的,反而一个键和对应的值是保存在同一个结构当中的
我们将这种能够保存一个键 + 对应值的结构称之为Entry(键值对):Entry = key + value - Entry本身是一个定义在Map接口中的内部接口,在HashMap中,Entry接口的实现类是Node,Node类是HashMap的内部类
- 通过HashMap的对象来获取所有的键值对(Entry)对象:
public class TestHashMap2 {
public static void main(String[] args) {
HashMap<String, Person> map = new HashMap<String, Person>();
map.put("张三", new Person("张三", 22, "111111"));
map.put("李四", new Person("李四", 23, "222222"));
map.put("王五", new Person("王五", 20, "333333"));
map.put("陈六", new Person("陈六", 21, "444444"));
map.put("田七", new Person("田七", 24, "555555"));
/*
* Entry和Entry之间:
* 1.是无序的,就是因为Entry的无序排列,才导致Map中键值对的无序性
* 2.不可能重复:虽然键值对之间的值有可能重复,但是键是不可能重复的,所以两个键值对之间不可能重复,也就是Entry之间不存在重复元素
* 综上所述:同一个Map中的Entry是无序不重复的
* 所以:可以使用Set集合保存Map中的所有Entry对象
*/
/*
* Set集合:有上述讨论得知,所有的键值对对象应该保存在一个Set集合中
*
* Set<Entry>:每一个键值对对象就是一个Entry对象,将一个Map中所有的键值对都封装为Entry之后
* 将这些Entry放在Set集合中,所以Set集合的泛型类型为Entry类型
*
* Set<Entry<String, Person>>:每一个Entry对象中,都保存着一对key和value
* 为了指定这个key和value的类型,所以Entry也具有泛型
* Entry的泛型应该匹配HashMap的泛型类型定义
*
* Map通过方法entrySet()返回当前Map中所有键值对对象的Set集合
*/
Set<Entry<String, Person>> entries = map.entrySet();
/*
* Iterator ite是从Map中得到所有键值对对象集合的Set中抽取的迭代器
* 所以当前迭代器中保存的对象类型一定和Set中元素类型保持一致
* 所以是Entry<String, Person>
*/
Iterator<Entry<String, Person>> ite = entries.iterator();
Entry<String, Person> entry = null; //临时变量
while(ite.hasNext()) {
entry = ite.next();
System.out.println("Entry [key=" + entry.getKey() + ", value=" + entry.getValue() + "]");
}
}
}
注意:HashSet的内部实现就是HashMap,每一个添加进入HashSet中的元素,都相当于内部HashMap的键,内部HashMap的值是一个Object类型的常量,这就巧妙的利用HashMap实现HashSet中元素无序不重复的需求。
Map接口的其他实现类
public class TestMap {
public static void main(String[] args) {
String name1 = "张三"; //强引用的String对象
Person p1 = new Person("张三", 22, "111111"); //强引用的Person对象
String name2 = "李四";
Person p2 = new Person("李四", 23, "222222");
///[0]设置HashMap的对照组
HashMap<String, Person> map = new HashMap<String, Person>();
map.put(name1, p1);
map.put(new String("张三"), p1);
/*
* 对象的强弱引用:
* 对象的强引用:如果一个对象本体,具有保存其内存地址的变量在引用它,我们认为这个对象本体是强引用对象
* 对象的弱引用:如果一个对象本体在内存中的地址,没有保存在任何引用变量中,我们成这样的对象为弱引用对象
*/
map.put(new String("王五"), new Person("王五", 26, "333333"));
/*
* HashMap中,认为键相同的条件是,两个键对象的内容相同
* 也就是equals方法返回true
* 我们就认为两个键是相同的
*/
// System.out.println(map)
//[1]IdentityHashMap
/*
* 强Hash表:
* 强在对“键相同”这个条件的判别上:
* 在IdentityHashMap中,只有两个键严格复合==比较返回true
* 我们才认为两个键是重复
* 如果两个键的内容相同,但是所在的内存地址不同,
* 在IdentityHashMap中,也算作是两个不同的键
*/
IdentityHashMap<String, Person> iMap = new IdentityHashMap<String, Person>();
iMap.put(name1, p1);
iMap.put(name2, p2);
iMap.put(new String("张三"), p1);
System.out.println(iMap);
//[2]WeakHashMap
/*
* 弱Hash表:
* 弱在对键的引用上
* 在一般的HashMap中,如果键值对的键是弱引用对象,及时运行垃圾回收机制,这个键值对也不会被回收
* 但是在WeakHashMap当中,如果一个键值对的键是一个弱引用对象
* 那么如果垃圾回收机制运行,这个以弱引用为键的键值对将会被从Map中删除
*/
WeakHashMap<String, Person> wMap = new WeakHashMap<String, Person>();
wMap.put(name1, p1);
wMap.put(name2, p2);
wMap.put(new String("王五"), new Person("王五", 26, "333333"));
//通过下列代码手动执行JVM的垃圾回收机制
System.gc();
System.runFinalization();
System.out.println("----------");
System.out.println(map);
System.out.println(wMap);
}
}
TreeMap
properties
Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存属性集。不过Properties有特殊的地方,就是它的键和值都是字符串类型。
所以大多数是用它来做为配置文件来进行使用的
properties的常用方法
- load(InputStream inStream)
- store(OutputStream out, String comments)
- getProperty/setProperty
load(InputStream inStream)
这个方法可以从.properties属性文件对应的文件输入流中,加载属性列表到Properties类对象。如下面的代码:
Properties pro = new Properties();
FileInputStream in = new FileInputStream("a.properties");
pro.load(in);
in.close();
store(OutputStream out, String comments)
这个方法将Properties类对象的属性列表保存到输出流中。如下面的代码:
FileOutputStream oFile = new FileOutputStream(file, "a.properties");
pro.store(oFile, "Comment");
oFile.close();
- 如果comments不为空,保存后的属性文件第一行会是#comments,表示注释信息;如果为空则没有注释信息。
- 注释信息后面是属性文件的当前保存时间信息。
getProperty/setProperty
这两个方法是分别是获取和设置属性信息。
实际代码如下:
name=root
pass=liu
读取db.properties属性列表。代码如下:
public class PropertiesDemo01 {
public static void main(String[] args) throws IOException {
pro.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
System.out.println(pro.getProperty("name"));
System.out.println(pro.getProperty("pwd"));
System.out.println(pro.setProperty("url"));
}
}
collections工具类
在collections中有很多的方法,但是能用到的方法几乎很少,我们来看一下都有哪些方法。
void sort(List) //对 List 容器内的元素排序,按照升序进行排序。
void shuffle(List) //对 List 容器内的元素进行随机排列
void reverse(List) //对 List 容器内的元素进行逆续排列
void fill(List, Object) //用一个特定的对象重写整个 List 容器
int binarySearch(List, Object)//采用折半查找的方法查找特定对象
这些就是collections的常用方法,基本都是基于List的,应为只有List接口的实现类是有序可重复的,其他的Set和Map接口的实现类都是无序的,所以在这个工具类中我们经常用到的就是针对List接口的实现类的升序、随机、翻转、折半查找、特定对象重写等方法。