Java基础复习(十)
Map:接口
- HashMap:底层是哈希表,线程不安全的。
- TreeMap:底层是二叉树,线程不安全的。
Collection和Map的区别
Collection:直接存储的是值。
Map:本身是接口,存储的是键(key)值(value)对,一个元素就是一个键值对key必须是唯一的,值随意,即可以重复.
Map基础
public class Demo1 {
public static void main(String[] args) {
//介绍Map接口的方法
Map<String, String> map = new HashMap<>();
//1.增加
//V put(K key,V value) 增加一个键值对
String value1 = map.put("01", "java");//返回null
String value2 = map.put("02", "html");//返回null
String value3 = map.put("02", "iOS");//返回html
map.put("03", "BigData");
map.put("04", "iOS");
//关于返回值,如果当前的key之前没有添加过,返回null.如果当前的key之前已经存在了,这里返回之前的值
System.out.println(value3);
System.out.println(map);
//void putAll(Map<? extends K,? extends V> map) 增加多个
//2.删除
//V remove(Object key) 根据key删除元素
//返回值就是被删掉的值
//System.out.println(map.remove("01"));
//void clear() 删除全部 != null
//3.获取
//V get(Object key) 根据key查找元素
System.out.println(map.get("02"));
//int size() 获取键值对的个数
//Set<K> keySet() 遍历方法一
//Set<Map.Entry<K,V>> entrySet() 遍历方法二
//4.常用的判断
//boolean isEmpty() //空map!=null
//map = null;
System.out.println(map.isEmpty());
//boolean containsKey(K key) 是否包含当前的key
//boolean containsValue(V value) 是否包含当前的value
}
}
Map的遍历方法
Set keySet()
遍历方法一:先得到所有的key,放入一个Set中,利用Set的迭代器进行遍历得到key,在利用key获取value
Set<Map.Entry<K,V>> entrySet()
遍历方法二:先得到所有的entry,放入一个Set中,利用Set的迭代器进行遍历得到entry实体,在利用entry的方法获取key和value
遍历方法三:map.forEach(new BiConsumer<String, String>()
遍历方法四:增强for循环for(Map.Entry<String,String>entry:map.entrySet())
package test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
public class demo1 {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("01", "java");
map.put("02", "html");
map.put("05", "iOS");
map.put("03", "BigData");
map.put("04", "iOS");
//增强for循环
for (Map.Entry<String,String> entry:map.entrySet())
{
System.out.println(entry+" "+entry.getKey()+" "+entry.getValue());
}
//foreach遍历
map.forEach(new BiConsumer<String, String>() {
@Override
public void accept(String s, String s2) {
System.out.println(s+" "+s2);
}
});
test(map);
test2(map);
}
public static void test(Map<String, String> map)
{
//得到装着key的set
Set<String> set = map.keySet();
//遍历set,得到key,再根据key获取value
Iterator<String> iterator = set.iterator();
while (iterator.hasNext())
{
String key = iterator.next();
System.out.println(key+" "+map.get(key));
}
}
public static void test2(Map<String , String>map)
{
//先得到装着Entry实体的set
Set<Map.Entry<String,String>> set = map.entrySet();
//遍历Set,得到entry实体,再调用entry实体对象的方法获取key和value
Iterator<Map.Entry<String,String>> iterator = set.iterator();
while (iterator.hasNext())
{
Map.Entry<String, String> entry = iterator.next();
//通过setValue可以将map的原始值改变,但是一般在使用entrySet的时候,是进行遍历.不进行值的改变.
//entry.setValue("bingbing");
System.out.println(entry.getKey()+" "+entry.getValue());
}
}
}
HashMap
去重,因为HashMap的底层与HashSet的底层实现一样,只是对HashMap去重的时候,操作的是key
①HashMap的工作原理
HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
-
1.HashMap的底层实现:哈希表
-
2.HashMap与HashTable的区别
- 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢
- HashMap是基于哈希表实现的
- HashMap是非线程安全的,只是用于单线程环境下,多线程环境下可以采用concurrent并发包下的concurrentHashMap。
- HashMap内部维护了一个存储数据的Entry数组,HashMap采用链表解决冲突,每一个Entry本质上是一个单向链表。
- 线程安全以及速度
3.HashMap和HashSet的区别
- HashSet实现了Set接口,它不允许集合中有重复的值,在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。
- HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。
TreeMap
去重和排序,底层与TreeSet一致,在进行排序去重的时候就是去操作key.
练习题二: 1.josgjsjagwajsogiseafgjwsjgvoier
要求:1.转化成字符串 : a(字符的个数)b()c()… 2.区分大小写 3.只读取字母
package test;
import javax.swing.table.TableRowSorter;
import java.util.*;
public class demo2 {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
String str = in.next();
String strs[] = str.split("");
String regex = "[a-zA-Z]";
TreeMap<String,Integer> treeMap = new TreeMap<>();
for(int i =0;i<str.length();i++)
{
if(strs[i].matches(regex))
{
//包含的话,利用TreeMap的排序(对key排序)去重(查看key是否重复),实现值得增加,key值不变。
if(treeMap.containsKey(strs[i]))
{
int num = treeMap.get(strs[i]);
System.out.println("ppp"+num);
treeMap.put(strs[i],++num);
System.out.println(treeMap);
}
//不存在,存入treeMao中
else {
treeMap.put(strs[i],1);
}
}
}
//方法一
Set<Map.Entry<String , Integer>> set = treeMap.entrySet();
Iterator<Map.Entry<String,Integer>> iterator = set.iterator();
while (iterator.hasNext())
{
Map.Entry<String, Integer> entry = iterator.next();
System.out.println(entry.getKey()+" "+entry.getValue());
}
//方法二
/*Set<String> set = treeMap.keySet();
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String oo = it.next();
System.out.println(oo + " " + treeMap.get(oo));
}*/
}
}
注意点
- 什么类型的数据类型可以作为key?
- a:实现了Comparable接口的compareTo()方法
- b:实现了Comparator接口的compare()方法
- 可以的代表:String,包装类,自定义的实现了要求的类
- 不可以的代表:数组,ArrayList,LinkedList(如果给他们建立的比较器也可以比较,但是不建议使用)
2.
哈希表
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
-
-
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,它是基于快速存取的角度设计的,也是一种典型的“空间换时间”的做法。顾名思义,该数据结构可以理解为一个线性表,但是其中的元素不是紧密排列的,而是可能存在空隙。
-
它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度
-
2.hash表扩容的理解
可是当哈希表接近装满时,因为数组的扩容问题,性能较低(转移到更大的哈希表中).
Java默认的散列单元大小全部都是2的幂,初始值为16(2的4次幂)。假如16条链表中的75%链接有数据的时候,则认为加载因子达到默认的0.75。HahSet开始重新散列,也就是将原来的散列结构全部抛弃,重新开辟一个散列单元大小为32(2的5次幂)的散列结果,并重新计算各个数据的存储位置。以此类推下去…
3.排重机制的实现
假如我们有一个数据(散列码76268),而此时的HashSet有128个散列单元,那么这个数据将有可能插入到数组的第108个链表中(76268%128=108)。但这只是有可能,如果在第108号链表中发现有一个老数据与新数据equals()=true的话,这个新数据将被视为已经加入,而不再重复丢入链表。
4.优点
哈希表的插入和查找是很优秀的.
对于查找:直接根据数据的散列码和散列表的数组大小计算除余后,就得到了所在数组的位置,然后再查找链表中是否有这个数据即可。因为数组本身查找速度快,所以查找的效率高低体现在链表中,但是真实情况下在一条链表中的数据又很少,有的甚至没有,所以几乎没有什么迭代的代价。所以散列表的查找效率建立在散列单元所指向的链表中数据的多少上.
对于插入:数组的插入速度慢,而链表的插入速度快.当我们使用哈希表时,不需要更改数组的结构,只需要在找到对应的数组下标后,进入对应的链表,操作链表即可.所以hash表的整体插入速度也很快.
-
-