开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
框架体系图
Collection:
Map:
Collection
常用方法:
-
添加
add(Object obj)addAll(Collection coll)
-
获取有效元素的个数
int size()
-
清空集合
void clear()
-
是否为空集合
boolean isEmpty()
-
是否包含某个元素
boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
boolean containsAll(Collection c):也是调用元素的equals方法来比较的。拿两个集合的元素挨个比较
-
删除
boolean remove(Object obj):通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
boolean removeAll(Collection coll):取当前集合的差集,移除coll的所有元素
-
取两个集合的交集
boolean retainAll(Collection c):把交集的结果存在当前集合中,不影响c
-
集合是否相等
boolean equals(Object obj)
-
转成对象数组
Object[] toArray()
-
数组转为List集合
-
Arrays.asList(T... a): 形参如果是基本数据类型数组,会被当成一个整体元素- 小坑:返回的ArrayList是Arrays类自己定义的一个静态内部类,而不是java.util包下的ArrayList,这个内部类没有实现add()、remove()方法,而是直接使用它的父类AbstractList的相应方法,而AbstractList中的add()和remove()是直接抛出java.lang.UnsupportedOperationException异常的。
-
-
遍历
-
iterator():返回迭代器对象,用于集合遍历while (iterator.hasNext()){ System.out.println(iterator.next()); }hasNext():根据指针判断是否还有下一个元素next():①指针下移 ②将下移以后集合位置上的元素返回remove():根据指针删除,少用慎用
-
增强for循环(forEach循环)
-
普通for循环
-
List
有序的,可重复的,又称之为动态数组。
- ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用
Object[] elementData存储 - LinkedList:对于频繁的插入和删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
- Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用
Object[] elementData存储
常用方法:
void add(int index, Object ele):在index位置插入ele元素
boolean addAll(int index, Collection eles):从index位置开始将eles的所有元素添加进来Object get(int index):获取指定index位置的元素int indexOf(Object obj):返回obj在集合中首次出现的位置int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置Object remove(int index):移除指定index位置的元素,并返回此元素Object set(int index, Object ele):设置指定index位置的元素为eleList subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的集合,左闭右开,不会影响原本的集合
ArrayList
- JDK1.7:使用空参构造器创建一个初始容量为10的数组;默认情况扩容为原本的1.5倍
- JDK1.8:使用空参构造器创建一个初始容量为0的数组,当添加第一个元素时再创建一个容量为10的数组;后续添加和扩容操作与JDK1.7一致
LinkedList
双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
- prev变量记录前一个元素的位置
- next变量记录后一个元素的位置
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Vector
无参构造器创建时底层是一个长度为10的数组;默认扩容为原来的2倍。
Set
无序性:指的是添加的时候不是从0开始存储的。不等于随机性。
不可重复性:不只用equals()判断。
Set接口没有额外定义新的方法,使用的都是Collection中声明的方法。
- HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
- LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历
- TreeSet:可以按照添加对象的指定属性进行排序
HashSet
添加元素过程:
- 向HashSet添加元素a,首先调用a所在类的
hashCode()方法,计算a的哈希值 - 此哈希值通过某种算法计算出在HashSet底层数组的存放位置(即索引位置),判断数组此位置是是否已经有元素
-
此位置没有其他元素,则a添加成功——情况1
-
此位置有其他元素b(或以链表形式存在多个元素),则比较a与b的hash值
-
hash值不相同,则a添加成功——情况2
-
hash值相同,调用a所在类的
equals()方法equals()返回false,则a添加成功——情况3equals()返回true,a添加失败
-
对于添加成功的情况2和情况3而言:a与指定索引的元素以链表的方式存储。
- JDK1.7:a放到数组中,指向原来的元素
- JDK1.8:原来的元素放在数组中,指向a
HashSet底层:使用HashMap来作为存储结构,value指向同一个Object。
LinkedHashSet
- LinkedHashSet插入性能略低于HashSet,但在遍历Set时有很好的性能。
- 在HashSet的基础上使用双向链表维护元素的顺序,可以根据插入顺序进行遍历。
Set set = new LinkedHashSet();
set.add(new String("AA"));
set.add(456);
set.add(new Customer("刘德华", 1001));
TreeSet
-
向TreeSet添加的元素,必须是相同类的对象。
-
TreeSet是根据
compareTo(obj)或compare(Object o1,Object o2)是否为0判断元素是否相同 -
两种排序方式:
- 自然排序:
compareTo(obj),在元素类中
- 定制排序:
compare(Object o1,Object o2),在构造器中,匿名内部类(可用Lambda表达式代替)
- 自然排序:
Map
存储key-value对的数据。key-value构成了一个Entry对象。
-
HashMap:作为Map的主要实现类;线程不安全的,效率高;可以存储null的key和value
- LinkedHashMap:保证在遍历map元素中,可以按照添加的顺序实现遍历
-
TreeMap:可以按照key进行自然排序或定制排序
-
Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
- Properties:常用来处理配置文件;key和value都是String类型
常用方法:
-
Object put(Object key,Object value):如果key之前有对应的value,则返回对应value,否则返回null -
void putAll(Map m):将m中的所有key-value对存放到当前map中 -
Object remove(Object key):移除指定key的key-value对,并返回value -
void clear():清空当前map中的所有数据。不是map = null -
Object get(Object key):获取指定key对应的value -
boolean containsKey(Object key):是否包含指定的key。先找哈希值,再equals -
boolean containsValue(Object value):是否包含指定的value -
int size():返回map中key-value对的个数 -
boolean isEmpty():判断当前map是否为空 -
boolean equals(Object obj):判断当前map和参数对象obj是否相等 -
遍历
Set keySet():返回所有key构成的Set集合Collection values():返回所有value构成的Collection集合Set entrySet():返回所有key-value对构成的Set集合
HashMap
JDK7
HashMap map = new HashMap();
//....执行多次put
map.put(key1, value1);
-
实例化之后,底层创建了长度为16的一维数组Entry[] table
-
调用key1所在类的
hashCode()计算key1的哈希值,此哈希值经过某种算法计算之后,得到在Entry数组中的存放位置-
如果此位置上的数据为空,则Entry添加成功
-
如果此位置上的数据不为空,比较key1和已经存在的一个或多个数据(以链表形式存在)的哈希值
-
如果key1的哈希值和已经存在的数据哈希值都不相同,则Entry添加成功 ----情况1
-
如果和某一个数据(key2-value2)的哈希值相同,调用key1所在类的
equals(key2)方法进行比较- 返回false,则Entry添加成功 ----情况2
- 返回true,使用value1替换value2
-
-
关于情况1和情况2,此时的Entry和原来的数据以链表的方式存储
扩容:在到达扩容的临界值且此位置上的数据不为空,扩容为原来容量的2倍。
源码中的重要常量:
- DEFAULT_INITIAL_CAPACITY: HashMap的默认容量,16
- DEFAULT_LOAD_FACTOR:HashMap的默认加载因子,0.75
- threshold:扩容的临界值,= 容量 * 加载因子,16 * 0.75 =>12
JDK8
相较于JDK7在底层实现方面的不同:
- 实例化时底层没有创建数组,在首次调用
put()方法时创建长度为16的数组 - 底层数组是Node[],而非Entry[]。只是单纯改个名,本身属性没有变
- JDK7底层结构:数组+链表。JDK8:数组+链表+红黑树
- 形成链表结构时,新添加的key-value对在链表的尾部
- 当数组在某一个索引位置上的元素(以链表形式存在)> 8且数组长度 > 64时,此索引位置上的所有数据改为使用红黑树存储
源码中新增的常量:
- TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树
- MIN_TREEIFY_CAPACITY:Node被树化时最小的Node数组长度,64
面试八股
负载因子(加载因子,填充比)值的大小,对HashMap有什么影响?
- 负载因子的大小决定了HashMap的数据密度。
- 负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,造成查询或插入时的比较次数增多,性能会下降。
- 负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。
- 按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数。
LinkedHashMap
HashMap的子类,能够记录添加元素的先后顺序。
HashMap中的内部类:Node
static class Node<K, V> implements Map.Entry<K, V> {
final int hash;
final K key;
V value;
Node<K, V> next;
}
LinkedHashMap中的内部类:Entry
static class Entry<K, V> extends HashMap.Node<K, V> {
Entry<K, V> before, after; //能够记录添加元素的先后顺序
Entry(int hash, K key, V value, Node<K, V> next) {
super(hash, key, value, next);
}
}
TreeMap
- 添加的key必须是相同类的对象
- 按照key进行自然排序或定制排序
Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类
常用方法
排序操作
reverse(List):反转 List 中元素的顺序shuffle(List):对 List 集合元素进行随机排序sort(List):根据元素的自然排序对指定List排序sort(List,Comparator):根据指定的 Comparator 产生的定制排序对 List 集合元素进行排序swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
查找、替换
-
Object max(Collection):根据元素的自然排序,返回给定集合中的最大元素 -
Object max(Collection,Comparator):根据Comparator指定的定制排序,返回给定集合中的最大元素 -
Object min(Collection) -
Object min(Collection,Comparator) -
int frequency(Collection,Object):返回指定集合中指定元素的出现次数 -
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值 -
void copy(List dest,List src):将src中的内容复制到dest中-
需满足dest.size() >= src.size() ;size():集合元素个数
解决方案:借助Arrays工具类
List src = new ArrayList(); src.add("Aa"); src.add("Aa"); List dest = Arrays.asList(new Object[src.size()]);//相当于创建src.size()个null的集合 System.out.println(dest); Collections.copy(dest, src);
-
同步控制
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
所以实际开发中根本用不到TreeSet,Vector,Hashtable等,常用的就ArrayList和HashMap,因为他们都可以通过Collections工具类完成各种操作。