Java 容器类
1. Java 中的常见容器类
1.1 几种常见的集合类及特点
常见的集合类: ArrayList,LinkedList,Vector,HashSet, TreeSet,HashMap , HashTable
list 中的元素可重复,set ,map 中的元素不可重复。
1.1.1 List
List:
-
ArrayList 底层结构是数组,底层查询快,增删慢。
ArrayList:基于数组实现,非线程安全的,效率高,便于索引,但不 便于插入删除;数组是空间连续的内存空间,每次在做数据的插入和删除,涉及到其他元素的移动,所以效率较慢;
-
LinkedList 底层结构是链表型的,增删快,查询慢。
LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还 存储下一个元素的地址。链表增删快,查找慢;
-
voctor 底层结构是数组 线程安全的,增删慢,查询慢
Vector:基于数组实现,线程安全的,效率低
1.1.2 Map
Map 接口有三个实现类(HashMap:基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null 键;HashTable:线程安全,低效,不支持 null 值和 null 键;LinkedHashMap:是 HashMap 的一个子类,保存了 记录的插入顺序;SortMap 接口:TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序)。
1.1.3 Set
Set 接口有两个实现类(HashSet:底层是由 HashMap 实现,不允许集合中有重复的值,使用该方式时需要重 写 equals()和 hashCode()方法;LinkedHashSet:继承与 HashSet,同时又基于 LinkedHashMap 来进行实现,底 层使用的是 LinkedHashMp)。
1.1.4 List,Set,Map 间的差异性对比
-
List 和 Set 是存储单列数据的集合,Map 是存储键和值这样的双列数据的集合;
-
List 中存储的数据是有顺序,并且允许重复;Map 中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的,Set 中存储的数据是无序的,且不允许有重复,但元素在集合中的位置由元素的 hashcode 决定,位置是固定的
(Set 集合根据 hashcode 来进行数据的存储,所以位置是固定的,但是位置不是用户可以控制的,所以对于用户来说 set 中的元素还是无序的);
比较 List Set Map 继承接口 Collection Collection 常见实现类 ArrayList,LinkedList,Vector HashSet,LinkedHashSet,TreeSet HashMap,HashTable ,TreeMap,LinkedHashMap 是否可重复 可重复 不可重复 不可重复 顺序 有序 无序(由HashCode决定数据位置),LinkedHashSet底层是链表加哈希表。是有序的。 hashMap 是无序的,(由HashCode决定数据位置)。LinkedHashMap 是有序的
1.1.5 HashSet 和 HashMap
hashSet 的 底层是由 hashMap 实现的,HashSet 的值存放在 HashMap 的key 上,HashMap 的 value 统一是一个常量PRESENT。
1.1.6 ArrayList 和 LinkedList 的区别
ArrayList 的底层是数组,支持随机访问,LinkedList 的底层是双向循环链表,不支持随机访问。
1.1.7 Array 和 ArrayList 的区别
Array 只能存纳 基本数据类型的数据, 而ArrayList 只能容纳对象。
1.1.8 HashMap 和 HashTable 的区别
HashMap 是线程不安全的,HashMap 是一个接口( Map 的一个子接口),是将键映射到值得对象,不允许键值重复允许空键和空值;由于非线程安全,HashMap 的效率要较 HashTable 的效率高一些。
HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值; HashTable中所有的方法都被synchronized关键字
修饰,多个线程访问时不需要自己为它的方法实现同步,而 HashMap 在被多个线程访问的时 候需要自己为它的方法实现同步;
2. 容器类的扩容
Class | 初始大小 | 加载因子 | 扩容倍数 | 底层实现 | Code | 是否线程安全 | 同步方式 |
---|---|---|---|---|---|---|---|
ArrayList | 10 | 1 | 1.5倍 | Object数组 | int newCapacity = oldCapacity + (oldCapacity >> 1); ">>"右移符号,所以是除以2,所以新的容量是就的1.5倍 Arrays.copyOf 调用 System.arraycopy 扩充数组 | 线程不安全 | - |
Vector | 10 | 1 | 2倍 | Object数组 | int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity); capacityIncrement默认为0,所以是加上本身,也就是2*oldCapacity,2倍大小 Arrays.copyOf 调用 System.arraycopy 扩充数组 | 线程安全 | synchronized |
HashSet | 16 | 0.75f | 2倍 | HashMap<E,Object> | add方法实际调用HashMap的方法put | 线程不安全 | - |
HashMap | 16 | 0.75f | 2倍 | Map.Entry | void addEntry(int hash, K key, V value, int bucketIndex) { if ((size >= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key) ? hash(key) : 0; bucketIndex = indexFor(hash, table.length); } createEntry(hash, key, value, bucketIndex); } | 线程不安全 | - |
Hashtable | 11 | 0.75f | 2倍+1 | Hashtable.Entry数组 | int newCapacity = (oldCapacity << 1) + 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) return; newCapacity = MAX_ARRAY_SIZE; } | 线程安全 | synchronized |
3. 如何实现对HashMap的排序
实现对HashMap排序(hashMap 是无序的,子类linkedHashMap是有序的)
import java.util.*; import java.util.stream.Collectors; public class VolatileDemo { public static int num = 0; public static void main(String[] args) { HashMap<Integer, User> users = new HashMap<>(); users.put(1, new User("张三", 25)); users.put(3, new User("李四", 22)); users.put(2, new User("王五", 28)); HashMap sortMap = sortHashMap(users); } private static HashMap sortHashMap(HashMap<Integer, User> users) { HashMap sortMap = new LinkedHashMap<Integer,User>(); Set<Map.Entry<Integer, User>> set = users.entrySet(); ArrayList<Map.Entry<Integer, User>> entries = new ArrayList<>(set); Collections.sort(entries, (o1,o2)->{ return o1.getValue().getAge() - o2.getValue().getAge(); }); entries.forEach((o)->{sortMap.put(o.getKey(),o.getValue());}); Map<Integer, User> collect = entries.stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); Map<Integer, User> sortMap1 = set.stream().sorted((o1, o2) -> { return o1.getValue().getAge() - o2.getValue().getAge(); }).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); System.out.println(collect); System.out.println(sortMap); //正确排序 System.out.println(sortMap1); return sortMap; } } class User{ private int age; private String name ; public User(String name, int age) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String naem) { this.name = naem; } @Override public String toString() { return "User{" + "age=" + age + ", name='" + name + '\'' + '}'; } }
4. ConcurrentHashMap
4.1 HashMap 和 ConcurrentHashMap 的区别
ConcurrentHashMap是线程安全的HashMap的实现。
- ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上 都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更 好,而HashMap没有锁机制,不是线程安全的。
- HashMap的键值对允许有null,但是ConCurrentHashMap都不允许。
5. HashTable
5.1 线程安全类的安全性分析
Hashtable 线程安全方法组合分析
线程安全类中的每个方法都能保证线程安全,但是线程安全方法的组合
并不一定能够保证线程安全
Hashtable table = new Hashtable();
if(table.get("key") == null){
table.put("key",value);
}