Set接口实现类——LinkedHashSet(HashSet的子类)
-
基本介绍
- LinkedHashSet是 HashSet的子类
- LinkedHashSet底层是一个 LinkedHashMap(HashMap的子类),底层维护了一个数组+双向链表
- LinkedHashSet根据元素的 hashCode值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的。(添加顺序和取出顺序一致)
- LinkedHashSet不允许添重复元素
/*
源码:
添加第一次时,直接将数组table扩容到16,存放的结点类型是LinkedHashMap$Entry
数组是HashMap$Node[],存放的元素/数据是LinkedHashMap$Entry
*/
Map接口
Map接口实现类的特点
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
- Map 中的key 和 value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key 不允许重复,原因和HashSet一样,前面分析过源码,key相同时,value值被新输入的值替换
- Map中的value可以重复
- Map 的key可以为 null, value也可以为null,注意key为null,只能有一个,value为null ,可以多个
- 常用String类作为Map的key
- key 和 value之间存在单向一对一关系,即通过指定的key 总能找到对应的value
- Map存放数据的key-value示意图,一对k-v是放在一个HashMap$Node中的,有因为Node实现了Entry 接口,有些书上也说一对k-v就是一个Entry(如图)[代码演示]
Map接口的常用方法
Map map = new HashMap();
//put:添加
map.put("鹿晗","关晓彤");
// remove:根据键删除映射关系
map.remove(null);
System.out.println("map=" + map);
// get: 根据键获取值
Object val = map.get("鹿晗");
System.out.println("val=" + val);//关晓彤
// size:获取元素个数
System.out.println("k-v=" + map.size());//1
// isEmpty:判断个数是否为 0
System.out.println(map.isEmpty());//F
// clear:清除 k-v
//map.clear();
System.out.println("map=" + map);//全都没了
// containsKey:查找键是否存在
System.out.println("结果=" + map.containsKey("hsp"));//T
Map接口六大遍历方式:
工具方法:
containsKey:查找键是否存在
keySet:获取所有的键
entrySet:获取所有关系k-v
values:获取所有的值
//第一组: 先取出 所有的 Key , 通过 Key 取出对应的 Value
Set keyset = map.keySet();
//(1) 增强 for
System.out.println("-----第一种方式-------");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2) 迭代器
System.out.println("----第二种方式--------");
Iterator iterator = keyset.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println(key + "-" + map.get(key));
}
//第二组: 把所有的 values 取出
Collection values = map.values();
//这里可以使用所有的 Collections 使用的遍历方法
//(1) 增强 for
System.out.println("---取出所有的 value 增强 for----");
for (Object value : values) {
System.out.println(value);
}
//(2) 迭代器
System.out.println("---取出所有的 value 迭代器----");
Iterator iterator2 = values.iterator();
while (iterator2.hasNext()) {
Object value = iterator2.next();
System.out.println(value);
}
//第三组: 通过 EntrySet 来获取 k-v
Set entrySet = map.entrySet();
// EntrySet<Map.Entry<K,V>>
//(1) 增强 for
System.out.println("----使用 EntrySet 的 for 增强(第 3 种)----");
for (Object entry : entrySet) {
//将 entry 转成 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
//(2) 迭代器
System.out.println("----使用 EntrySet 的 迭代器(第 4 种)----");
Iterator iterator3 = entrySet.iterator();
while (iterator3.hasNext()) {
Object entry = iterator3.next();
//System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
//向下转型 Map.Entry
Map.Entry m = (Map.Entry) entry;
System.out.println(m.getKey() + "-" + m.getValue());
}
HashMap小结:
- Map接口的常用实现类:HashMap、Hashtable和Properties
- HashMap是 Map 接口使用频率最高的实现类。
- HashMap是以 key-val对的方式来存储数据(HashMap$Node类型)[案例Entry ]
- key不能重复,但是值可以重复,允许使用null键和null值。
- 如果添加相同的key,则会覆盖原来的key-val ,等同于修改.(key不会替换,val会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.(jdk8的hashMap底层数组+链表+红黑树)
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
HashMap底层机制及源码剖析
HashTable
-
存放的元素是键值对:即K-V
-
hashtable的键和值都不能为null,否则会抛出NullPointerException
-
hashTable使用方法基本上和HashMap一样
-
hashTable是线程安全的(synchronized), hashMap 是线程不安全的
-
简单看下底层结构 HashTable的应用案例
Properties
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据。
- 他的使用特点和Hashtable类似
- Properties还可以用于从 xoxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
- 说明:工作后 Xxx.properties文件通常作为配置文件,这个知识点在IO流举例,有兴趣可先看文章
//老韩解读
//1. Properties 继承 Hashtable
//2. 可以通过 k-v 存放数据, 当然 key 和 value 不能为 null
//增加
Properties properties = new Properties();
//properties.put(null, "abc");//抛出 空指针异常
//properties.put("abc", null); //抛出 空指针异常
properties.put("john", 100);//k-v
properties.put("lucy", 100);
properties.put("lic", 100);
properties.put("lic", 88);//如果有相同的 key , value 被替换
System.out.println("properties=" + properties);
//通过 k 获取对应值
System.out.println(properties.get("lic"));//88
//删除
properties.remove("lic");
System.out.println("properties=" + properties);
//修改
properties.put("john", "约翰");
System.out.println("properties=" + properties);
开发中如何选择集合实现类
Collections工具类
- Collections是一个操作 Set、List和 Map等集合的工具类
- Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
排序操作:
-
reverse(List):反转 List中元素的顺序
-
shuffle(List):对List集合元素进行随机排序
-
sort(List):根据元素的自然顺序对指定List 集合元素按升序排序
-
sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
-
swap(List,int,int):将指定list集合中的i处元素和j处元素进行交换
-
//创建 ArrayList 集合, 用于测试. List list = new ArrayList(); list.add("tom"); list.add("smith"); list.add("king"); list.add("milan"); list.add("tom"); // reverse(List): 反转 List 中元素的顺序 Collections.reverse(list); System.out.println("list=" + list); // shuffle(List): 对 List 集合元素进行随机排序 // for (int i = 0; i < 5; i++) { // Collections.shuffle(list); // System.out.println("list=" + list); // } // sort(List): 根据元素的自然顺序对指定 List 集合元素按升序排序 Collections.sort(list); System.out.println("自然排序后"); System.out.println("list=" + list); // sort(List, Comparator): 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 //我们希望按照 字符串的长度大小排序 Collections.sort(list, new Comparator() { @Override public int compare(Object o1, Object o2) { //可以加入校验代码. return ((String) o2).length() - ((String) o1).length(); } }); System.out.println("字符串长度大小排序=" + list); // swap(List, int, int): 将指定 list 集合中的 i 处元素和 j 处元素进行交换 //比如 Collections.swap(list, 0, 1); System.out.println("交换后的情况"); System.out.println("list=" + list); //Object max(Collection): 根据元素的自然顺序, 返回给定集合中的最大元素 System.out.println("自然顺序最大元素=" + Collections.max(list)); //Object max(Collection, Comparator): 根据 Comparator 指定的顺序, 返回给定集合中的最大元素 //比如, 我们要返回长度最大的元素 Object maxObject = Collections.max(list, new Comparator() { @Override public int compare(Object o1, Object o2) { return ((String)o1).length() - ((String)o2).length(); } }); System.out.println("长度最大的元素=" + maxObject); //Object min(Collection) //Object min(Collection, Comparator) //上面的两个方法, 参考 max 即可 //int frequency(Collection, Object): 返回指定集合中指定元素的出现次数 System.out.println("tom 出现的次数=" + Collections.frequency(list, "tom")); //void copy(List dest,List src): 将 src 中的内容复制到 dest 中 ArrayList dest = new ArrayList(); //为了完成一个完整拷贝, 我们需要先给 dest 赋值, 大小和 list.size()一样 for(int i = 0; i < list.size(); i++) { dest.add(""); } //拷贝 Collections.copy(dest, list); System.out.println("dest=" + dest); //boolean replaceAll(List list, Object oldVal, Object newVal): 使用新值替换 List 对象的所有旧值 //如果 list 中, 有 tom 就替换成 汤姆 Collections.replaceAll(list, "tom", "汤姆"); System.out.println("list 替换后=" + list);