集合

集合是java中提供的一种容器,可以用来存储多个数据

类层次结构

mindmap    容器        Collection            List[List]                CopyOnWriteList                ArrayList                Vector                    Stack                LinkedList            Set(Set)                HashSet                    LinkedHashSet                SortedSet                    TreeSet                CopyOnWriteArraySet                ConcurrentSkipListSet            Queue((Queue))                Deque{{Deque}}                    ArrayDeque                    BlockingDeque                        LinkedBlockingDeque                BlockingQueue))BlockingQueue((                    ArrayBlockingQueue                    PriorityBlockingQueue                    LinkedBlockingQueue                    TransferQueue                        LinkedTransferQueue                    SynchronousQueue                PriorityQueue                ConcurrentLinkedQueue                DelayQueue        Map             HashMap                LinkedHashMap            TreeMap            WeakHashMap            IdentityHashMap

可以学习人家的接口是怎么划分的,这个类虽然接口众多,但是职责却很清晰

以及人家是如何复用已有的代码来实现新功能

Iterator迭代器

Iterator<String> it = coll.iterator();while(it.hasNext()){ //判断是否有迭代元素    String s = it.next();//获取迭代出的元素    System.out.println(s);}

List

List 集合的遍历结果是稳定的

常用方法

Queue

Map集合

批注 2019-08-02 100926

常用子类

Map集合类KeyValueSuperJDK说明
Hashtable不允许为null不允许为nullDictionary1.0线程安全(过时)
ConcurrentHashMap不允许为null不允许为nullAbstractMap1.5锁分段技术或CAS(JDK8及以上)
TreeMap不允许为null允许为nullAbstractMap1.2线程不安全(有序)
HashMap允许为null允许为nullAbstractMap1.2线程不安全( resize死链问题)

在任何Map中 都要避免KV设置为null

并发场景下 数据丢失

死链问题:并发情况下链表修改导致的不一致问题

JDK11后取消了分段锁机制 引入了红黑树结构 put remove size等操作都是用了CAS

并非一定要覆写hashCode与equals 其内部元素时通过Comparable与Comparator来实现key去重排序的

常用方法

Set集合

HashSet

底层使用hashmap

批注 2019-08-02 095407

存储自定义类型元素时,需要重写对象中的hashCode和equals方法

TreeSet

底层使用TreeMap 保证Key有序

LinkedHashSet

有序的哈希集合

Collections 工具类

集合初始化

这样如果存放在集合的元素比较多 就会造成不断扩容 影响性能

所以集合初始化时应该指定好默认值

数组与集合

new int[-1]; // 运行时异常:NegativeArraySizeException:-1

数组遍历优先使用foreach方式

数组转集合

注意转集合的过程中是否使用了视图的方式:

Arrays.asList(...)这个方法返回了一个不可变的ArrayList(Arrays的内部类),不能进行修改操作 否则会抛出异常

集合转数组

Object[] objects = list.toArray();// 泛型丢失String[] arr1 = new String[2];list.toArray(arr1); // arr1为[null,null]String[] arr2 = new String[3];list.toArray(arr2); // arr2为[1,2,3]

当toArray传入的数组容量比size小时 该方法就会弃用这个数组 而是自己创建一个数组返回

当数组容量等于size时 运行时最快的,空间效率也是最高的

集合与泛型

//第一段:泛型出现之前的集合定义方式List al = new ArrayList();al.add (new Object());al.add (new Integer(111));al.add(new String("hello alal"));//第二段:把a1引用赋值给a2,注意a2与al的区别是增加了泛型限制<object>List<Object> a2 = al;a2.add (new Object());a2.add (new Integer(222));a2.add(new String("hello a2a2"));//第三段:把a1引用赋值给a3,注意a3与al的区别是增加了泛型<Integer>List<Integer> a3 = al;a3.add(new Integer (333));下方两行编译出错,不允许增加非Integer类型进入集合a3.add(new object());a3.add(new String("hello a3a3"));//第四段:把a1引用赋值给a4,al与a4的区别是增加了通配符List<?>a4 = al;//允许副除和清除元素al.remove(O);a4.clear();// 编译出错。不允许增加任何元素a4.add (new Object());

<? extends T> put功能受限 ?只能是T及T的子类型

<? super T> get 功能受限 ?只能是T及T的父类型

元素的比较

Comparable和Comparator两个接口的区别:

hashCode 与 equals

通过哈希将数据分散开来

// HashMap 判断两个key是否相等if (e.hash == hash &&    ((k = e.key) == key || (key != null && key.equals(k))))

快速失败机制

当前线程维护一个expectedModCount 遍历之前这个值等于modCount

如果在遍历的过程中发现 expectedModCount != modCount 就代表集合被别的线程修改了 这时候会跑出一个ConcurrentModificationException

这个时候得使用迭代器来实现在遍历中修改集合的功能

并发集合都是使用快速失败机制实现的 集合修改与遍历没有任何关系 但这种机制会导致读取不到最新的数据 也是CAP理论中 A与P的矛盾

注意事项

线程安全

Collections 帮我们实现了 List、Set、Map 对应的线程安全的方法

synchronized打头的方法可以将指定的集合包装成线程安全的集合

集合性能

在 List 和 Map 大量数据新增的时候,我们不要使用 for 循环 + add/put 方法新增,这样子会有很大的扩容成本,我们应该尽量使用 addAll 和 putAll 方法进行新增

ArrayList的remove方法,删除之后都会对被删除位置的元素进行移动,如果进行循环remove,会造成性能问题,可以采用removeAll方法,这个批量删除接口只会对数组的元素移动一次

集合的坑

JAVA7到JAVA8集合的升级

Guava

工厂模式初始化

HashMap<Object, Object> map = Maps.newHashMap();

Lists

ArrayList<String> list = Lists.newArrayList();ArrayList<Object> objects = Lists.newArrayListWithCapacity(10);// 不知道精确值,给出一个模糊值ArrayList<Object> objects1 = Lists.newArrayListWithExpectedSize(20);// 反转一个list,并非物理反转,而是通过对传入index的处理实现的var list = Lists.reverse(list)// list拆分var list = Lists.partition(list,3)

Maps