Java为数据结构中的映射定义了一个接口java.util.Map,其由诸多实现类,其中四个实现类分别是
HashMap、HashTable、LinkedHashMap和TreeMap
。
Map用于存储键值对,根据键得到值,因此不允许键重复,值可以重复。
【1】定义与特性
① HashMap
HashMap是一个最常用的Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。
HashMap最多只允许一条记录的键为null,允许多条记录的值为null。
HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致。
如果需要同步,可以用
Collections.synchronizedMap(HashMap map)
方法使HashMap具有同步的能力。或者使用ConcurrentHashMap。
② HashTable
Hashtable与HashMap类似,不同的是:
它不允许记录的键或者值为空;它支持线程的同步
,为线程安全。
即任一时刻只有一个线程能写Hashtable,然而,这也导致了HashTable在写入时会比HashMap较慢。
③ LinkedHashMap
LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的。
在遍历的时候会比HashMap慢。有HashMap的全部特性。对比HashMap只是有序和无序差别。
④ TreeMap
TreeMap能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器。
当用Iteraor遍历TreeMap时,得到的记录是排过序的。TreeMap的键和值都不能为空。
⑤ ConcurrentHashMap
ConcurrentHashMap 类(是 Java并发包 java.util.concurrent 中提供的一个线程安全且高效的 HashMap 实现)。
HashTable 是使用 synchronize 关键字加锁的原理(就是对对象加锁),多个线程竞争一把锁,容易阻塞。
ConcurrentHashMap JDK 1.7 中使用分段锁(
ReentrantLock + Segment + HashEntry
),相当于把一个 HashMap 分成多个段,每段分配一把锁,这样支持多线程访问。锁粒度:基于 Segment,包含多个 HashEntry。
JDK 1.8 中使用
CAS + synchronized + Node + 红黑树
。锁粒度:Node(首结点)(实现 Map.Entry)。锁粒度降低了。
HashMap 的键值对允许有null,但是ConCurrentHashMap 都不允许。
【2】实例代码
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.TreeMap;
public class TestMap {
public static void init(Map map){
if (map != null){
String key = null;
for (int i=5; i>0; i--){
key = new Integer(i).toString() + ".0";
map.put(key, key.toString());
//Map中的键是不重复的,如果插入两个键值一样的记录,
//那么后插入的记录会覆盖先插入的记录
map.put(key, key.toString() + "0"); }
}
}
public static void output(Map map){
if (map != null){
Object key = null;
Object value = null;
//使用迭代器遍历Map的键,根据键取值
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
//或者使用迭代器遍历Map的记录Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//一个Map.Entry代表一条记录
entry = (Map.Entry)it.next();
//通过entry可以获得记录的键和值
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
}
}
}
public static boolean containsKey(Map map, Object key){
if (map != null){
return map.containsKey(key);
}
return false;
}
public static boolean containsValue(Map map, Object value){
if (map != null){
return map.containsValue(value);
}
return false;
}
public static void testHashMap(){
Map myMap = new HashMap();
init(myMap);
//HashMap的键可以为null
myMap.put(null,"ddd");
//HashMap的值可以为null
myMap.put("aaa", null);
output(myMap);
}
public static void testHashtable(){
Map myMap = new Hashtable();
init(myMap);
//Hashtable的键不能为null
//myMap.put(null,"ddd");
//Hashtable的值不能为null
//myMap.put("aaa", null);
output(myMap);
}
public static void testLinkedHashMap(){
Map myMap = new LinkedHashMap();
init(myMap);
//LinkedHashMap的键可以为null
myMap.put(null,"ddd");
myMap.put(null,"aaa");
//LinkedHashMap的值可以为null
myMap.put("aaa", null);
output(myMap);
}
public static void testTreeMap(){
Map myMap = new TreeMap();
init(myMap);
//TreeMap的键不能为null
//myMap.put(null,"ddd");
//TreeMap的值不能为null
//myMap.put("aaa", null);
output(myMap);
}
public static void main(String[] args) {
System.out.println("采用HashMap");
TestMap.testHashMap();
System.out.println("采用Hashtable");
TestMap.testHashtable();
System.out.println("采用LinkedHashMap");
TestMap.testLinkedHashMap();
System.out.println("采用TreeMap");
TestMap.testTreeMap();
Map myMap = new HashMap();
TestMap.init(myMap);
System.out.println("新初始化一个Map: myMap");
TestMap.output(myMap);
//清空Map
myMap.clear();
System.out.println("将myMap clear后,myMap空了么? " + myMap.isEmpty());
TestMap.output(myMap);
myMap.put("aaa", "aaaa");
myMap.put("bbb", "bbbb");
//判断Map是否包含某键或者某值
System.out.println("myMap包含键aaa? "+ TestMap.containsKey(myMap, "aaa"));
System.out.println("myMap包含值aaaa? "+ TestMap.containsValue(myMap, "aaaa"));
//根据键删除Map中的记录
myMap.remove("aaa");
System.out.println("删除键aaa后,myMap包含键aaa? "+ TestMap.containsKey(myMap, "aaa"));
//获取Map的记录数
System.out.println("myMap包含的记录数: " + myMap.size());
}
}
输出结果:
采用HashMap
key: null; value: ddd
key: 3.0; value: 3.00
key: aaa; value: null
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用Hashtable
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 3.0; value: 3.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
采用LinkedHashMap
key: 5.0; value: 5.00
key: 4.0; value: 4.00
key: 3.0; value: 3.00
key: 2.0; value: 2.00
key: 1.0; value: 1.00
key: null; value: aaa
key: aaa; value: null
采用TreeMap
key: 1.0; value: 1.00
key: 2.0; value: 2.00
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 5.0; value: 5.00
新初始化一个Map: myMap
key: 3.0; value: 3.00
key: 4.0; value: 4.00
key: 1.0; value: 1.00
key: 5.0; value: 5.00
key: 2.0; value: 2.00
将myMap clear后,myMap空了么? true
myMap包含键aaa? true
myMap包含值aaaa? true
删除键aaa后,myMap包含键aaa? false
myMap包含的记录数: 1
源码分析
遍历Map有两种方法:
(1)map的keySet()方法获得键的集合,再调用键集合的iterator方法获得键的迭代器,以此迭代地取出Map中的键,用get方法获得键对应的值,便完成了Map的遍历。
代码如下所示:
//使用迭代器遍历Map的键,根据键取值
Iterator it = map.keySet().iterator();
while (it.hasNext()){
key = it.next();
value = map.get(key);
System.out.println("key: " + key + "; value: " + value );
}
(2)使用Map的entrySet方法获得Map中记录的集合,每条对象都是一个Map.Entry对象,使用其getKey方法获得记录的键,使用其getValue方法获得记录的值。
代码如下所示:
//或者使用迭代器遍历Map的记录Map.Entry
Map.Entry entry = null;
it = map.entrySet().iterator();
while (it.hasNext()){
//一个Map.Entry代表一条记录
entry = (Map.Entry)it.next();
//通过entry可以获得记录的键和值
//System.out.println("key: " + entry.getKey() + "; value: " + entry.getValue());
【3】 区别图示
类别\特性
|
速度
|
线程安全
|
键值为null
|
次序
|
HashMap
|
快
|
非
|
允许一条记录键为null,允许多条记录的值为null
|
无序
|
HashTable
|
比HashMap慢
|
是
|
不允许记录的键或者值为空
|
无序
|
LinkedHashMap
|
比HashMap慢
|
非
|
允许一条记录键为null,允许多条记录的值为null
|
保持了插入的次序
|
TreeMap
|
比HashMap慢
|
非
|
键和值都不能为空
|
默认键升序排列,也可以自定义排序
|
ConcurrentHashMap
|
比HashMap慢
|
是
|
不允许记录的键或者值为空
|
无序
|
【4】 总结
一般情况下,我们用的最多的是HashMap,在Map 中插入、删除和定位元素,HashMap 是最好的选择。
但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。
如果需要输出的顺序和输入的相同,那么用LinkedHashMap 可以实现,它还可以按读取顺序来排列。
如果想实现并发中安全,则使用ConcurrentHashMap。
java中预约系统代码 java代码预热
背景某服务调用,因服务器性能问题,无法直接使用最大qps进行调用,需要动态加速逻辑设置最大加速时间,设置允许加速到的最大qps代码import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.atomic.AtomicLong;
* 预热控制器
* @author chuny
MySQL表格路径 mysql数据路径
# MySQL的数据目录1. MySQL8的主要目录结构1.1 数据库文件的存放路径MySQL数据库文件的存放路径:/var/lib/mysql/MySQL服务器程序在启动时会到文件系统的某个目录下加载一些文件,之后在运行过程中产生的数据也都会存储
到这个目录下的某些文件中,这个目录就称为数据目录.
MySQL把数据都存到哪个路径下呢?其实数据目录对应着一个系统变量datadir,我们在使用客户端
之前通过在java环境中使用opencv库对矩形进行了识别,本次将继续使用java语言利用opencv库对图片中的人脸进行识别。
首先在主函数中要加载本地库,否则将会抛出java.lang.UnsatisfiedLinkError异常。
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);接下来通过文件路径加载文件
(string1==string2)
但在java中,这个代码即使在两个字符串完全相同的情况下也会返回false
Java中必须使用string1.equals(string2)来进行判断
string s1="Hello";
string s2="Hello";