javaSE_集合与队列(map)

 Map:健值对,健不能,和set一样,有HashMapLinkedHashMapTreeMap

HashMap,基于散列表实现,查找速度快(依赖hashcode()equals()),存放元素无序。

ConcurrentHashMap,线程安全的HashMap,用于替代HashTable(线程安全,但基于整个对象的锁实现的,效率不高),而ConcurrentHashMap是采用分段加锁的方式实现,提升了效率。

LinkedHashMap,基于散列表、双向链表实现。如HashMap的查找速度,遍历时有序(默认为插入顺序,可通过构造方法设置“最近最少使用(Least Recently Used)顺序”)。

TreeMap,基于红黑树实现,存放有序(依赖Compareable)。

 

1) HashMap实现原理,如何解决冲突

HashMap使用Hash表,即数组+链表的方式存储数据。

存储时,通过key.hash获取一个hash的数值,再模上hash表的长度,得到key值在hash表中的索引位置。如果此位置为空则直接添加值;如果此位置有值,则将新的key放到此位置上,同时新值对应的Entrynext指向之前的值,即在链表的头部插入数据。

优化:当数据量越来越多,链表就会越来越长,性能也就越差。HashMap里面设置了一个因子,随着Entrysise越来越大,Entry[]会以一定的规则加长长度。

 javaSE_集合与队列(map)

2) HashTable实现原理

它的每个方法都是synchronized类型,对hashtable的任何操作都会把整个表锁住,因此是线程安全的。当put数据时,会先计算hash值,如果此位置没有元素则直接添加,如d1,如果已有元素,会按照链表的方式将元素插入在链表的头部,如aa 

 javaSE_集合与队列(map)

3) ConcurrentHashMap实现原理

内部使用Segment数组,每个Segment类似于HashTable,操作时是锁住某个Segment对象,其他线程可以并发操作其他Segment对象,因此效率要比HashTable.

 javaSE_集合与队列(map) 

4) hashcode()equals()

hashcode()equals()即用于识别对象的身份。

HashMap或类似的实现中,查找一个对象,是通过hashcode()返回的散列值映射到一个范围内的下标,在通过equals()比较此下标连接的链表是否存在相同的对象。

简单来说,hashcode()用于参考、快速定位(缩减范围),真正是否等于是依赖equals()

 

覆盖这两个方法有什么原则呢?

equals()的覆盖,主要是基于此对象的业务。

hashcode()的覆盖原则,详情可参见《Effective Java》的“覆盖equals时总要覆盖hashcode”一节。

有几个比较重要的原则:

1、两个equals相等的对象,其hashcode是相等的。

2、两个equals不等的对象,其hashcode有可能是相等的。

3、好的hashcode()应产生分布均匀的散列码。

 

和set一样,mapkey值是不能重复的,如果是自定义对象,是通过hashcodeequals来判断是为同一个对象的,因此如果以对象作为key值,需要注意这两个方法的重载。

 

5) LinkedHashMap两种遍历方式

public static void useLinkedHashMap(){

System.out.println("--LinkedHashMap根据输入的顺序输出--");  

LinkedHashMap<String, String> linkedHashMap=new LinkedHashMap<>();  

linkedHashMap.put("3", "Value3");  

linkedHashMap.put("1", "Value1");  

linkedHashMap.put("2", "Value2");  

linkedHashMap.put("b", "ValueB");  

linkedHashMap.put("a", "ValueA");

linkedHashMap.get("2");

linkedHashMap.get("b");

Iterator lit = linkedHashMap.entrySet().iterator();  

while (lit.hasNext()) {  

Map.Entry e = (Map.Entry) lit.next();  

System.out.println(e.getKey() + "--" + e.getValue());  

}

System.out.println("--LinkedHashMap最近最少使用排序--");

// 初始容量、加载因子使用默认的160.75,排序为true

LinkedHashMap<String, String> Map2 = new LinkedHashMap<>(16, 0.75f, true);

Map2.put("3", "Value3");  

Map2.put("1", "Value1");  

Map2.put("2", "Value2");  

Map2.put("b", "ValueB");  

Map2.put("a", "ValueA");

Map2.get("b");

Map2.get("2");

Iterator lit2 = Map2.entrySet().iterator();  

while (lit2.hasNext()) {  

Map.Entry e = (Map.Entry) lit2.next();  

System.out.println(e.getKey() + "--" + e.getValue());  

}

}

输出

--LinkedHashMap根据输入的顺序输出--

3--Value3

1--Value1

2--Value2

b--ValueB

a--ValueA

--LinkedHashMap最近最少使用排序--

3--Value3

1--Value1

a--ValueA

b--ValueB

2--Value2

b2刚使用,因此排在了最后。

 

6) 自定义对象之HashMap

public static void useCustomObjMap(){

System.out.println("--HashMap使用自定义对象--");

Map<Student, String> map = new HashMap<>();

map.put(new Student("张三", 30), "1");

map.put(new Student("李四", 32), "2");

map.put(new Student("王五", 31), "3");

map.put(new Student("李四", 33), "4");

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()){

Map.Entry e = (Entry) iter.next();

Student st = (Student) e.getKey();

System.out.println(st.getName() + "--" + e.getValue());

}

}

输出

--treeMap使用自定义对象--

王五--3

李四--2

李四--4

张三--1

见,有两个李四,虽然姓名一样,但是这是两个对象,因此可以重复。

如果重载studenthashcodeequals方法,以姓名为判断两对象是否相同的依据

public class Student {

private String name;

private Integer age;

public Student(String name, Integer age){

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

@Override

public int hashCode() {

final int prime = 31;

int result = 1;

result = prime * result + ((name == null) ? 0 : name.hashCode());

return result;

}

@Override

public boolean equals(Object obj) {

if (this == obj)

return true;

if (obj == null)

return false;

if (getClass() != obj.getClass())

return false;

Student other = (Student) obj;

if (name == null) {

if (other.name != null)

return false;

} else if (!name.equals(other.name))

return false;

return true;

}

}

输出

--HashMap使用自定义对象--

王五--3

李四--4

张三--1

结论hashMap如果以自定义对象为key值,需要注意其hashcodeequals

 

7) 自定义对象之TreeMap

上面的hashMap改为TreeMap运行时直接报错:

public static void useCustomObjMap(){

System.out.println("--TreeMap使用自定义对象--");

Map<Student, String> map = new TreeMap<>();

map.put(new Student("张三", 30), "1");

map.put(new Student("李四", 32), "2");

map.put(new Student("王五", 31), "3");

map.put(new Student("李四", 33), "4");

Iterator iter = map.entrySet().iterator();

while (iter.hasNext()){

Map.Entry e = (Entry) iter.next();

Student st = (Student) e.getKey();

System.out.println(st.getName() + "--" + e.getValue());

}

}

Exception in thread "main" java.lang.ClassCastException: com.gary.set.Student cannot be cast to java.lang.Comparable

说student类需要重载Comparable

public class Student implements Comparable{

private String name;

private Integer age;

public Student(String name, Integer age){

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Integer getAge() {

return age;

}

public void setAge(Integer age) {

this.age = age;

}

@Override

public int compareTo(Object arg0) {

Student st = (Student) arg0;

return this.name.hashCode() - st.name.hashCode();

}

}

时输出:

--TreeMap使用自定义对象--

张三--1

李四--4

王五--3

结论TreeMap使用自定义对象key值,其类需要重载Comparable

 

8) Map的遍历

 

四种方法遍历:

public static void traverseMap(){

HashMap<String, String> hashMap=new HashMap<>();  

hashMap.put("3", "Value3");  

hashMap.put("1", "Value1");  

hashMap.put("2", "Value2");  

hashMap.put("b", "ValueB");  

hashMap.put("a", "ValueA");  

// 方法

System.out.println("------entrySet------");

for (Map.Entry<String, String> entry : hashMap.entrySet()) {  

    System.out.println(entry.getKey() + "--" + entry.getValue());

}  

// 方法二:此方法比entrySet10%

// 遍历key

System.out.println("------keySet------");

for (String key : hashMap.keySet()){

System.out.println(key);

}

// 遍历值

System.out.println("------values------");

for (String value : hashMap.values()){

System.out.println(value);

}

// 方法三

// 性能和keySet的性能差不多,而且可以在循环体中调用it.remove来删除数据,上面的则不能

System.out.println("------iterator------");  

Iterator it = hashMap.entrySet().iterator();  

while (it.hasNext()) {  

Map.Entry e = (Map.Entry) it.next();  

System.out.println(e.getKey() + "--" + e.getValue());  

}

// 方法四

// 每次使用get方法取值是相当慢的

System.out.println("------keySet get------");

for (String key : hashMap.keySet()){

System.out.println(key + "--" + hashMap.get(key));

}

}

上所述,方法使用iterator是最好的选择,速度快,而且还可以执行删除操作