Map
List、Set接口都是Collection的子接口,Map接口是与Collection完全独立的另外一个体系
List &Set vs Map
List &Set & Collection只能操作单个元素,Map可以操作一对元素,因为Map存储结构是key-value映射。
Map接口定义时使用了泛型 ,并且定义了两个泛型K和V,k表示key,规定键元素的数据类型,V表示value,规定值元素的数据类型
Map接口的实现类
- HashMap:存储一组无序,key不能重复,value可以重复的元素,线程不安全,效率高;可以存储null的键和值.
- LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历,原因:在原有的HashMap底层的结构基础上,添加了一对指针,分别指向前一个和后一个,对于频繁的遍历操作,此类的执行效率高于hashmap
- Hashtable:存储一组无序,key不可以重复,value可以重复的元素,线程安全,不能存储null的key和value
- TreeMap:存储一组有序,key不可以重复,value可以重复元素,可以按照key进行排序,底层使用的红黑树
hashMap常规使用
public class Test6 {
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put("h","Hello");
hashMap.put("w","Hello");
hashMap.put("j","Hello");
hashMap.put("s","Hello");
hashMap.put("m","Hello");
hashMap.put("e","Hello");
System.out.println(hashMap);
hashMap.remove("e");
System.out.println("删除之后"+hashMap);
hashMap.put("m","Model");
System.out.println("添加之后"+hashMap);
if (hashMap.containsKey("a")){
System.out.println("map中存在key=a");
}else {
System.out.println("集合中不存在key=a");
}
if (hashMap.containsValue("Java")){
System.out.println("集合中存在value=Java");
}else {
System.out.println("集合中不存在value=Java");
}
Set keys = hashMap.keySet();
System.out.println("集合中的key");
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
Collection values = hashMap.values();
for (Object value : values) {
System.out.println(value);
}
System.out.println("***********");
iterator = keys.iterator();
while (iterator.hasNext()){
String key =(String) iterator.next();
String value = (String) hashMap.get(key);
System.out.println(key+"-"+value);
}
}
}
hashMap用法与hashtable基本一样,他们的区别是,hashtable是线程安全的,但是性能较低,hashMap是非线程安全的,但是性能较高
HashMap,方法没有用synchronized修饰,所以是非线程安全的
Hashtable,方法用synchronized修饰,所以是线程安全的
Hashtable
public class Test7 {
public static void main(String[] args) {
Hashtable hashtable = new Hashtable();
hashtable.put("h","Hello");
hashtable.put("w","world");
hashtable.put("j","Java");
hashtable.put("s","JavaSE");
hashtable.put("m","JavaME");
hashtable.put("e","Javaee");
System.out.println(hashtable);
hashtable.remove("e");
System.out.println(hashtable);
System.out.println(hashtable.containsKey("a"));
System.out.println(hashtable.containsValue("Java"));
Set keys = hashtable.keySet();
System.out.println(keys);
Collection values = hashtable.values();
System.out.println(values);
}
}
HashMap和Hashtable,保存的元素都是无序的,Map的另外一个实现类Treemap主要功能是按照key对集合中元素进行排序
public class Test8 {
public static void main(String[] args) {
TreeMap treeMap = new TreeMap();
treeMap.put(3,"Java");
treeMap.put(5,"JavaME");
treeMap.put(1,"HELLO");
treeMap.put(6,"Javaee");
treeMap.put(2,"world");
treeMap.put(4,"JavaSE");
Set keys = treeMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String key =(String) iterator.next();
System.out.println(key+"--"+treeMap.get(key));
}
}
}
Map结构的理解
Map中的key:无序的,不可重复的,使用Set存储所有的key —>key所在的类要重写equals()和hashCode() (以hashmap为例)
Map中的value:无序的,可重复的,使用Collection存储所有的value
一个键值对:key-value构成了一个Entry对象。
Map中的entry:无序的,不可重复的,使用Set存储所有的entry
HashMap的底层实现原理?以jdk7为例说明:
HashMap map = new HashMap();
在实例化以后,底层创建了一个长度是16的一维数组Entry[ ] table
…假设此时的场景是该map集合已经执行过了多次put…
map.put(key1,value1):
首先,调用key1所在的类的hashCode()计算key1的哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功。情况1
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据(以链表的形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。情况2
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在的类的equals(key2):
- 如果equals()返回false:此时key1-value1添加成功 情况3
- 如果equals返回true:使用value1替换value2
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。在不断添加数据的过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,才回去扩容,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来,一旦扩容,元素对象的哈希值需要重新计算
JDK8相较于jdk7在底层实现方面的不同:
- new HashMap():底层没有创建一个长度为16的数组
- jdk8底层数组是Node[ ],而非Entry[ ]
- 首次调用put方法时,底层创建长度为16的数组
- jdk7底层结构只有:数组+链表,jdk8中底层结构:数组+链表+红黑树,当数组的某一个索引位置上的元素以链表形式存在的数据个数>8 且当前数组的长度 > 64时,此时此索引位置上的所有数据改为红黑树存储
几个重要参数:
- DEFAULT_INITIAL_CAPACITY:hashmap的默认容量,16
- DEFAULT_LOAD_FACTOR:hashmap的默认加载因子:0.75
- threshold:扩容的临界值,=容量填充因子:160.75=>12
- TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树:8
- MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
为什么hashmap要提前扩容而不是满了再扩容以及为什么它的的加载因子是0.75?
hashmap它底层的数组不一定会满,因为他可能会出现链表的情况,如果等到它满了再去扩容,就可能会导致出现链表的情况表较多,它是为了尽可能让hashmap中出现链表的情况变少,因为遍历的时候会影响效率,因此才会提前扩容,想要出现链表的情况尽可能少,那摩就得加载因子越小越好,但是如果加载因子太小了,那摩就会造成数组经常扩容,导致数组的利用率变低了,但是如果加载因子太大了,又会导致出现链表的情况变多,因此如果想要做到两者兼顾的情况,那就得有一个合适的加载因子,通过大量统计学测试,得出让加载因子为0.75比较好,是两种情况趋于平衡
LinkedHashMap的底层实现原理
记录了添加元素的顺序,因此是有序的集合
Collections工具类
Collection接口,List和Set的父接口。
Collections 不是接口,它是一个工具类,专门提供了一些对集合的操作,方便开发者去使用,完成相应的业务功能。
Collections针对集合工具类,Collection
Arrays针对数组的工具类,Array
/**
* java.util.Collection是集合工具类,用来对集合进行操作,部分方法如下:
* - public static <T> boolean addAll(Collection<T> C,T... elements)往集合中添加一些元素
* - public static void shuffle(List<?> list) 打乱顺序:打乱集合顺序
*/
public class Demo01Collections {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
// //往集合中添加多个元素
// list.add("a");
// list.add("b");
// list.add("c");
// list.add("d");
// public static <T> boolean addAll(Collection<T> c,T... elements): 往集合中添加一些元素。
Collections.addAll(list,"a","b","c","d");
System.out.println(list);
// public static void shuffle(List<?> list); 打乱集合顺序
Collections.shuffle(list);
System.out.println(list);
}
}
public class Person implements Comparable<Person> {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
// return 0; //认为元素都是相同的
// return this.age - o.age; //按照年龄升序排列
return o.age - this.age; //按照年龄降序排列
}
}
/**
* java.utils.Collections是集合工具类,用来对集合进行操作,部分方法如下:
* public static <T> void sort(list<T> list):将集合中的元素按照默认规则排序
*
*
* 注意:
* sort(List<T> list) 使用原理
* 被排序的集合里边存储的元素,必须实现Comparable接口,重写接口中的方法compareTo定义的排序规则
*
* Comparable接口的排序规则:
* 自己(this)- 参数 : 升序
*
*
*/
public class Demo02Sort {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
System.out.println(list); // [1,3,2]
//public static <T> void sort(list<T> list):将集合中的元素按照默认的规则进行排序
Collections.sort(list); //默认的是升序
System.out.println(list);
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("张三",18));
list1.add(new Person("李四",20));
list1.add(new Person("王五",15));
System.out.println(list1);
Collections.sort(list1);
}
}