一、其他的集合类
我们这里来介绍一点其他的集合类
1.1、Linkedhashmap
Linkedhashmap在原来的基础上维护了一个双向链表,用来维护,插入的顺序。
public class LinkedHashMapTest {
public static void main(String[] args){
Map<String,String> map = new LinkedHashMap<>(16);
map.put("a","m");
map.put("b","n");
map.put("c","o");
map.put("d","p");
map.put("e","q");
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()){
Map.Entry<String, String> next = iterator.next();
System.out.println(next.getKey());
System.out.println(next.getValue());
}
}
}
我们看一下Linkedhashmap的构造器
public LinkedHashMap(int initialCapacity) {
super(initialCapacity);
accessOrder = false;
}
这只是一个空参构造
原文对于accseeOrder的解释是这样的
accessOrder – the ordering mode - true for access-order, false for insertion-order
// accessOrder为true的话,则会把访问过的元素放在链表后面,放置顺序是访问的顺序 如果accessOrder为flase的话,则按插入顺序来遍历
可以用这个集合实现LRU算法
public class LRU<K,V> extends LinkedHashMap<K,V> {
private int max_capacity;
public LRU(){
super(16,0.75F,true);
max_capacity = 8;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > max_capacity;
}
}
1.2、TreeMap
TreeMap底层实现是红黑树,所以天然支持排序。
我们看一下她的源码
public TreeMap() {
comparator = null;
}
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;
}
final int compare(Object k1, Object k2) {
return comparator==null ? ((Comparable<? super K>)k1).compareTo((K)k2)
: comparator.compare((K)k1, (K)k2);
}
他有两个构造器,我们实现一下空参构造
Map<Dog,String> map = new TreeMap<>();
for (int i = 0; i < 100; i++) {
map.put(new Dog(),"a");
}
报错,没有定义比较器,我们定义一个比较器
Map<Integer,String> map = new TreeMap<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
for (int i = 0; i < 100; i++) {
map.put(i,"a");
}
// 我们发现已经自动排序了
System.out.println(map);
1.3、Collections
Collections是一个工具类,它给我们提供了一些常用的好用的操作集合的方法。
ArrayList<Integer> list = new ArrayList<>();
list.add(12);
list.add(4);
list.add(3);
list.add(5);
//将集合按照默认的规则排序,按照数字从小到大的顺序排序
Collections.sort(list);
System.out.println("list = " + list);
//将集合中的元素反转
Collections.reverse(list);
System.out.println("list = " + list);
//addAll方法可以往集合中添加元素,也可往集合中添加一个集合
Collections.addAll(list,9,20,56);
//打乱集合中的元素
Collections.shuffle(list);
System.out.println("list = " + list);
//Arrays.asList方法可以返回一个长度内容固定的List集合
List<String> list2 = Arrays.asList("tom", "kobe", "jordan", "tracy","westbook","yaoming","ace","stephen");
//按照字符串首字符的升序排列
Collections.sort(list2);
二、线程安全问题
2.1、并发修改异常
使用增强for循环中删除元素会抛异常,其实不仅仅是删除元素,增加元素也会导致异常
public void StrangerFor() {
for (Integer array:arrayList) {
arrayList.remove(array);
System.out.println(array);
}
}
迭代器是依赖于集合而存在的,在判断成功后,集合的中新添加了元素,而迭代器却不知道,所以就报错了,这个错叫并发修改异常。
- 迭代器迭代元素,迭代器修改元素。
- 集合遍历元素,集合修改元素(普通for)
2.2、线程安全
我们知道当多个线程同时操作共享资源时会有线程安全问题。我们试着去产生一下这个问题
ArrayListTest arrayListTest = new ArrayListTest();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
ArrayListTest.arrayList.add(1);
}).start();
}
System.out.println(arrayList.size());
最后添加的数据并不到1000个,总是差两三个
那怎么解决线程安全的问题啊。加锁,只能有一个线程去操作这个集合
三、加锁
3.1、HashTable
HashMap和HashTable区别
- HashMap允许将 null 作为一个 entry 的 key 或者 value,而 Hashtable 不允许。
- HashMap 把 Hashtable 的 contains 方法去掉了,改成 containsValue 和 containsKey。因为 contains 方法容易让人引起误解。
- HashTable 继承自 Dictionary 类,而 HashMap 是 Java1.2 引进的 Map interface 的一个实现。
- HashTable 的方法是 Synchronized修饰 的,而 HashMap 不是,这也是是否能保证线程安全的重要保障。
- Hashtable 和 HashMap 采用的 hash/rehash 算法都不一样。
- 获取数组下标的算法不同,
3.2、Vector
ArrayList和Vector的区别
- Vector是多线程安全的,线程安全就是说多线程访问同一代码,不会产生不确定的结果。而ArrayList不是,这个可以从源码中看出,Vector类中的方法很多,有synchronized进行修饰,这样就导致了Vector在效率上无法与ArrayList相比;
- 两个都是采用线性连续空间存储元素,但是当空间不足的时候,两个类的扩容方式是不同的。
- Vector是一种老的动态数组,是线程同步的,效率很低,一般不赞成使用。
3.3、CopyOnWriteList
CopyOnWriteList的核心就是写入的时候加锁,保证线程安全,读取的时候不加锁。不是一股脑,给所有的方法加锁。
3.4、ConcurrentHashMap
ConcurrentHashMap和HashMap的代码基本一样,只不过在有些操作上使用了cas,有些地方加了锁。
JDK8以前是头插法,JDK8后是尾插法,那为什么要从头插法改成尾插法?
- 因为头插法会造成循环链表
- JDK7用头插是考虑到了一个所谓的热点数据的点(新插入的数据可能会更早用到),但这其实是个伪命题,因为JDK7中rehash的时候,旧链表迁移新链表的时候,如果在新表的数组索引位置相同,则链表元素会倒置(就是因为头插), 所以最后的结果 还是打乱了插入的顺序 ,所以总的来看支撑JDK7使用头插的这点原因也不足以支撑下去了 ,所以就干脆换成尾插 一举多得。
我们尝试开辟50个线程,每个线程向集合中put100000个元素,测试两个类所需要的时间。
@Test
public void hashtableTest() throws InterruptedException {
final Map<Integer,Integer> map = new Hashtable<>(500000);
final CountDownLatch countDownLatch = new CountDownLatch(50);
System.out.println("----------------开始测试Hashtable------------------");
long start = System.currentTimeMillis();
for (int i = 0; i < 50; i++) {
final int j = i;
new Thread(()->{
for (int k = 0; k < 100000; k++) {
map.put(j*k,1);
}
countDownLatch.countDown();
}).start();
}
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println("hashtable:(end-start) = " + (end - start));
// ----------------开始测试ConcurrentHashMap------------------
System.out.println("----------------开始测试ConcurrentHashMap------------------");
final Map map2 = new ConcurrentHashMap<>(500000);
final CountDownLatch countDownLatch2 = new CountDownLatch(50);
start = System.currentTimeMillis();
for (int i = 0; i < 50; i++) {
final int j = i;
new Thread(()->{
for (int k = 0; k < 100000; k++) {
map2.put(j*k,1);
}
countDownLatch2.countDown();
}).start();
}
countDownLatch.await();
end = System.currentTimeMillis();
System.out.println("ConcurrentHashMap:(end-start) = " + (end - start));
}
最后我们发现ConcurrentHashMap真的比HashTable快了很多
四、guava提供了一些不可变集合
谷歌的程序员无聊的时候搞得一个工具包
版权声明:本文为weixin_43903639原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。