新手了解java 集合基础知识(二)
程序员文章站
2022-03-08 19:54:51
目录三、map1、hashmap2、treemap3.concurrenthashmap三、map 存储的双列元素,key是无序的,不可重复,而value是无序,可重复的。1、hashm...
三、map
存储的双列元素,key是无序的,不可重复,而value是无序,可重复的。
1、hashmap
public class hashmapdemo { private map map = null; public void init() { map = new hashmap(); map.put("a", "aaa"); map.put("b", "bbb"); map.put("c", "ccc"); system.out.println(map); } // 添加元素 public void testput() { // v put(k key, v value) :把指定的key和value添加到集合中 map.put("a1", "aaa"); map.put("b1", "bbb"); map.put("c1", "ccc"); system.out.println(map); // void putall(map<? extends k,? extends v>m) :把指定集合添加集合中 map map1 = new hashmap(); map1.put("e", "eee"); map1.put("f", "fff"); map.putall(map1); system.out.println(map); // default v putifabsent(k key, v value) :如果key不存在就添加 map.putifabsent("a", "hello"); system.out.println(map); map.putifabsent("g", "ggg"); system.out.println(map); } // 修改元素 public void testmodify() { // v put(k key, v value) :把集合中指定key的值修改为指定的值 map.put("a", "hello"); map.put("a", "world"); system.out.println(map); // 说明,当key相同时,后面的值会覆盖前面的值。 // default v replace(k key, v value) :根据key来替换值,而不做增加操作 object replace = map.replace("b1", "java"); system.out.println(replace); system.out.println(map); //default boolean replace(k key, v oldvalue,v newvalue) } // 删除元素 public void testremove() { // v remove(object key) :根据指定key删除集合中对应的值 object c = map.remove("c"); system.out.println(c); system.out.println(map); // default boolean remove(object key, objectvalue) :根据key和value进行删除 map.remove("b", "bbb1"); system.out.println(map); // void clear() :清空集合中所有元素 map.clear(); system.out.println(map); } // 判断元素 public void testjudge() { // boolean isempty() :判断集合是否为空,如果是返回true,否则返回false system.out.println(map.isempty()); // boolean containskey(object key) :判断集合中是否包含指定的key,包含返回true,否则返回false boolean flag = map.containskey("a"); system.out.println(flag); // true flag = map.containskey("a1"); system.out.println(flag); // false // boolean containsvalue(object value) :判断集合中是否包含指定的value,包含返回true,否则返回false flag = map.containsvalue("aaa"); system.out.println(flag); // true flag = map.containsvalue("aaa1"); system.out.println(flag); // false } // 获取元素 public void testget() { // int size() :返回集合的元素个数 int size = map.size(); system.out.println(size); // v get(object key) :根据key获取值,如果找到就返回对应的值,否则返回null object val = map.get("a"); system.out.println(val); val = map.get("a1"); system.out.println(val); // null // default v getordefault(object key, vdefaultvalue) :根据key获取值,如果key不存在,则返回默认值 val = map.getordefault("a1", "hello"); system.out.println(val); // collection<v> values() :返回集合中所有的value collection values = map.values(); for (object value : values) { system.out.println(value); } // set<k> keyset() :返回集合中所有的key set set = map.keyset(); for (object o : set) { system.out.println(o); } } // 迭代元素 public void testiterator() { // 第一种:通过key获取值的方式 set keyset = map.keyset(); iterator it = keyset.iterator(); while (it.hasnext()) { object key = it.next(); object val = map.get(key); system.out.println(key + "=" + val); } system.out.println("------------------------ "); // 第二种:使用for循环 for (object key : map.keyset()) { system.out.println(key + "=" + map.get(key)); } system.out.println("------------------------ "); // 第三种:使用map接口中的内部类来完成,在框架中大量使用 set entryset = map.entryset(); for (object obj : entryset) { map.entry entry = (map.entry) obj; system.out.println(entry.getkey() + "=" + entry.getvalue()); } } }
说明:在hashmap中键-值允许为空,但键唯一,值可重复。hashmap不是线程安全的。
2、treemap
是一个有序的集合,默认使用的是自然排序方式。
public class person implements comparable { private string name; private int age; @override public int compareto(object o) { if (o instanceof person) { person p = (person) o; return this.age - p.age; } return 0; } public person() {} public person(string name, int age) { this.name = name; this.age = age; } public int getage() { return age; } public void setage(int age) { this.age = age; } @override public string tostring() { return "person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
测试
public class teemapdemo { @test public void testinteger() { treemap tm = new treemap(); tm.put(3, 333); tm.put(2, 222); tm.put(11, 111); tm.put(2, 222); system.out.println(tm); } @test public void teststring() { treemap tm = new treemap(); tm.put("hello", "hello"); tm.put("world", "world"); tm.put("about", ""); tm.put("abstract", ""); system.out.println(tm); } @test public void testperson() { treemap tm = new treemap(new comparator(){ @override public int compare(object o1, object o2) { if (o1 instanceof person && o2 instanceof person) { person p1 = (person) o1; person p2 = (person) o2; return p1.getage() - p2.getage(); } return 0; } }); tm.put(new person("张三",18), null); tm.put(new person("李四",17), null); system.out.println(tm); } }
说明:从上面的代码可以发现,treemap的使用和treeset的使用非常相似,观察hashset集合的源代码可以看出,当创建 hashset集合时,其实是底层使用的是hashmap。
public hashset() { map = new hashmap<>(); }
hashset实际上存的是hashmap的key。
3.concurrenthashmap
在map集合中我们介绍了hashmap
,treemap
,在多线程的情况下这些集合都不是线程安全的,因此可能出现线程安全的问题。
在java中hashtable是一种线程安全的hashmap
,hashtable
在方法上与hashmap
并无区别,仅仅只是在方法使用了synchronized
以此来达到线程安全的目的,我们观察hashtable的源码。
public synchronized v get(object key) { entry<?,?> tab[] = table; int hash = key.hashcode(); int index = (hash & 0x7fffffff) % tab.length; for (entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (v)e.value; } } return null; }
以上是hashtable的get源码,可以看出它仅仅只是在方法上添加了锁,这大大降低了线程的执行效率,以牺牲效率的形式来达到目的,这显然不是我们在实际中想要的,因此我们需要一种既能在线程安全方面有保障,在效率上还可以的方法。
concurrenthashmap采用的是分段锁的原理,我们观察源码。
public v put(k key, v value) { return putval(key, value, false); } final v putval(k key, v value, boolean onlyifabsent) { if (key == null || value == null) throw new nullpointerexception(); int hash = spread(key.hashcode()); int bincount = 0; for (node<k,v>[] tab = table;;) { node<k,v> f; int n, i, fh; if (tab == null || (n = tab.length) == 0) tab = inittable(); else if ((f = tabat(tab, i = (n - 1) & hash)) == null) { if (castabat(tab, i, null, new node<k,v>(hash, key, value, null))) break; // no lock when adding to empty bin } else if ((fh = f.hash) == moved) tab = helptransfer(tab, f); else { v oldval = null; synchronized (f) { if (tabat(tab, i) == f) { if (fh >= 0) { bincount = 1; for (node<k,v> e = f;; ++bincount) { k ek; if (e.hash == hash && ((ek = e.key) == key || (ek != null && key.equals(ek)))) { oldval = e.val; if (!onlyifabsent) e.val = value; break; } node<k,v> pred = e; if ((e = e.next) == null) { pred.next = new node<k,v>(hash, key, value, null); break; } } } else if (f instanceof treebin) { node<k,v> p; bincount = 2; if ((p = ((treebin<k,v>)f).puttreeval(hash, key, value)) != null) { oldval = p.val; if (!onlyifabsent) p.val = value; } } } } if (bincount != 0) { if (bincount >= treeify_threshold) treeifybin(tab, i); if (oldval != null) return oldval; break; } } } addcount(1l, bincount); return null; }
从源码中可以看出concurrenthashmap
仅仅是在当有线程去操作当前数据的时候添加了锁,因此效率大大提高了。
在线程安全的情况下提高了效率。
总结
本篇文章就到这里了,希望能对你有所帮助,也希望您能够多多关注的更多内容!