第13章 集合框架
13.1 Collection集合
13.1.1 集合的概念
集合是java中提供的一种容器,可以用来存储多个数据。
集合和数组既然都是容器,它们有啥区别呢?
-
数组的长度是固定的。集合的长度是可变的。
-
数组中可以存储基本数据类型值,也可以存储对象,而集合中只能存储对象
集合主要分为两大系列:Collection和Map,Collection 表示一组对象,Map表示一组映射关系或键值对。
13.1.2 Collection接口
Collection 层次结构中的根接口。Collection 表示一组对象,这些对象也称为 collection 的元素。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。JDK 不提供此接口的任何直接实现:它提供更具体的子接口(如 Set无序集合 和 List有序集合、Queue队列)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
Collection<E>是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。
13.2 List 有序集合
List集合代表一个元素有序、可重复的集合。集合中每个元素都有对应的顺序索引。
13.2.1 List接口中常用方法
List作为Collection集合的子接口,不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法,如下:
List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。
1、添加元素
-
void add(int index, E ele)
-
boolean addAll(int index, Collection<? extends E> eles)
2、删除和替换元素
-
E remove(int index)
-
E set(int index, E ele)
3、获取元素
-
E get(int index)
-
List subList(int fromIndex, int toIndex)
4、获取元素索引
-
int indexOf(Object obj)
-
int lastIndexOf(Object obj)
13.2.2ListIterator迭代器
List 集合额外提供了一个 listIterator() 方法,该方法返回一个 ListIterator 列表迭代器对象, ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法:
-
void add():通过迭代器添加元素到对应集合
-
void set(Object obj):通过迭代器替换正迭代的元素
-
void remove():通过迭代器删除刚迭代的元素
-
boolean hasPrevious():如果以逆向遍历列表,往前是否还有元素。
-
Object previous():返回列表中的前一个元素。
-
int previousIndex():返回列表中的前一个元素的索引
-
boolean hasNext()
-
Object next()
-
int nextIndex()
13.2.3 List接口的实现类们
List接口的实现类有很多,常见的有:
ArrayList:动态数组
Vector:动态数组
LinkedList:双向链表
当然,还有很多List接口的实现类这里没有列出来,基础阶段先了解这几个。
13.2.4 ArrayList
ArrayList 是List 接口的大小可变数组的实现。实现了所有可选列表操作, 并允许包括null 在内的所有元素。除了实现List 接口外, 此类还提供一些方法来操作内部用来存储列表的数组的大小( 此类大致上等同于 vector 类, 但 vector 是同步的) 。
ArrayList 底层使用数组实现,特点就和数组一致:查询速度快,增删速度慢。
13.2.5 ArrayList与Vector的区别
Java的List接口的实现类中有两个动态数组的实现:Vector和ArrayList。
它们的底层物理结构都是数组,我们称为动态数组。
-
ArrayList是新版的动态数组,线程不安全,效率高,Vector是旧版的动态数组,线程安全,效率低。
-
动态数组的扩容机制不同,ArrayList扩容为原来的1.5倍,Vector扩容增加为原来的2倍。
13.2.6 LinkedList
LinkedList 和ArrayList不同,是一个链表结构。可当作堆栈、队列、双端队列 。链表是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。链表有单链表和双链表之分。
13.2.7 链表结构
1、单向链表
单向链表的每一个节点包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。正如地下党的联络方式,一级一级,单线传递
2、双向链表
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的 prev 指针
思考题:LinkedList是单向链表还是双向链表,为什么?
3、ArrayList 和LinkedList结构区别
13.3 Set 无序集合
Set 集合就像是一个罐子,将数据丢进来就行,因为是丢进来的,Set集合通常不会记忆元素的添加顺序(无序)。 Set接口的方法和Collection基本相同,但Set不允许包含重复的元素。
Set集合支持的遍历方式和Collection集合一样:foreach和Iterator。
Set的常用实现类有:HashSet、TreeSet、LinkedHashSet。
13.3.1 HashSet
HashSet是Set接口的典型实现,大多时候使用Set集合的时候都是使用这个实现类。因为使用Hash算法来储存元素,因此有很好的存储于查找性能。
1、HashSet 的特点
不能保证元素的排列顺序, 顺序可能与添加顺序不同, 顺序也有可能发生变化 HashSet 不是同步的, 如果多个线程同时访问一个HashSet , 假设有两个或者两个以上线程同时修改了HashSet 集合时, 则必须通过代码来保证其同步 集合元素值可以是null
当向HashSet 集合中存入一个元素时,会调用该对象的hashCode() 方法来得到该对象的hashCode 值,然后根据该hashCode 值决定该对象在HashSet 中的存储位置。
如果有两个元素通过equals() 方法比较返回true,但它们的hashCode()方法返回值不相等, HashSet 将会把它们存储在不同的位置,依然可以添加成功。
也就是说 HashSet 集合判断两个元素相等的标准是两个对象通过equals() 方法比较相等, 并且两个对象的hashCode() 方法返回值也相等。
Set set = new HashSet(); set.add( new Object() ); set.add( new Object() ); set.add( "hello" ); set.add( "hello" ); set.add( "你好" ); set.add( 123 ); set.add( 123 ); set.add( 234 ); set.remove( "你好“ ); set.remove( new Object() ); for (Object object : set) { System.out.println( object ); }
13.3.2 TreeSet
介绍Set集合的时候说过,Set基本是无序的,但凡事总有例外,TreeSet就是Set中的例外,它储存的数据是有序的!
Set set = new TreeSet (); set.add("8890"); set.add("123"); set.add("张三"); set.add("李四"); set.add("ABC"); for (String string : set) { System.out.println( string ); }
13.3.3 LinkedHashSet
LinkedHashSet
它继承自 HashSet
,因此具有哈希表的查找性能,同时又使用链表维护元素的插入顺序。
Set set = new TreeSet (); set.add("8890"); set.add("123"); set.add("张三"); set.add("李四"); set.add("ABC"); for (String string : set) { System.out.println( string ); }
13.4 Collections工具类
参考操作数组的工具类:Arrays。
Collections 是一个操作 Set、List 和 Map 等集合的工具类。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法:
-
public static <T> boolean addAll(Collection<? super T> c,T... elements)将所有指定元素添加到指定 collection 中。
-
public static <T> int binarySearch(List<? extends Comparable<? super T>> list,T key)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
-
public static <T> int binarySearch(List<? extends T> list,T key,Comparator<? super T> c)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且集合也事先必须是按照c比较器规则进行排序过的,否则结果不确定。
-
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,而且支持自然排序
-
public static <T> T max(Collection<? extends T> coll,Comparator<? super T> comp)在coll集合中找出最大的元素,集合中的对象必须是T或T的子类对象,按照比较器comp找出最大者
-
public static void reverse(List<?> list)反转指定列表List中元素的顺序。
-
public static void shuffle(List<?> list) List 集合元素进行随机排序,类似洗牌
-
public static <T extends Comparable<? super T>> void sort(List<T> list)根据元素的自然顺序对指定 List 集合元素按升序排序
-
public static <T> void sort(List<T> list,Comparator<? super T> c)根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
-
public static void swap(List<?> list,int i,int j)将指定 list 集合中的 i 处元素和 j 处元素进行交换
-
public static int frequency(Collection<?> c,Object o)返回指定集合中指定元素的出现次数
-
public static <T> void copy(List<? super T> dest,List<? extends T> src)将src中的内容复制到dest中
-
public static <T> boolean replaceAll(List<T> list,T oldVal,T newVal):使用新值替换 List 对象的所有旧值
-
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
-
Collections类中提供了多个unmodifiableXxx()方法,该方法返回指定 Xxx的不可修改的视图。
13.5 数据结构
数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这些运算后所得到的新结构仍然是原来的结构类型。
(1)数据的逻辑结构指反映数据元素之间的逻辑关系,而与他们在计算机中的存储位置无关:
-
散列结构:数据结构中的元素之间除了“同属一个集合” 的相互关系外,别无其他关系;
-
线性结构:数据结构中的元素存在一对一的相互关系;
-
树形结构:数据结构中的元素存在一对多的相互关系;
-
图形结构:数据结构中的元素存在多对多的相互关系。
(2)数据的物理结构/存储结构:是描述数据具体在内存中的存储(如:数组结构、链式结构、索引结构、哈希结构)等,一种数据逻辑结构可表示成一种或多种物理存储结构。
-
数组结构:元素在内存中是==连续存储==的,即元素存储在一整块连续的存储空间中,此时根据索引的查询效率是非常高的,因为可以根据下标索引直接一步到位找到元素位置,如果在数组末尾添加和删除元素效率也非常高。缺点是,如果事先申请足够大的内存空间,可能造成空间浪费,如果事先申请较小的内存空间,可能造成频繁扩容导致元素频繁搬家。另外,在数组中间添加、删除元素操作,就需要移动元素,此时效率也要打折。
-
链式结构:元素在内存中是不要求连续存储的,但是==元素是封装在结点==当中的,结点中需要存储元素数据,以及相关结点对象的引用地址。结点与结点之间可以是一对一的关系,也可以一对多的关系,比如:链表、树等。遍历链式结构只能从头遍历,对于较长的链表来说查询效率不高,对于树结构来说,查询效率比链表要高一点,因为每次可以确定一个分支,从而排除其他分支,但是相对于数组来说,还是数组[下标]的方式更快。树的实现方式有很多种,无非就是在添加/删除效率 与 查询效率之间权衡。
-
索引结构:元素在内存中是不要求连续存储的,但是需要有==单独的一个索引表==来记录每一个元素的地址,这种结构根据索引的查询效率很高,但是需要额外存储和维护索引表。
-
哈希结构:元素的存储位置需要通过其==hashCode值==来计算,查询效率也很多,但是要考虑和解决好哈希冲突问题。
数据结构和算法是一门完整并且复杂的课程。
Java的核心类库中提供很多数据结构对应的集合类型,例如动态数组、双向链表、顺序栈、链式栈、队列、双端队列、红黑树、哈希表等等。
13.6 栈
堆栈是一种先进后出(FILO:first in last out)或后进先出(LIFO:last in first out)的结构。
栈只是逻辑结构,其物理结构可以是数组,也可以是链表,即栈结构分为顺序栈和链式栈。
核心类库中的栈结构有Stack和LinkedList。Stack就是顺序栈,它是Vector的子类。LinkedList是链式栈。
体现栈结构的操作方法:
-
peek()方法:查看栈顶元素,不弹出
-
pop()方法:弹出栈
-
push(E e)方法:压入栈
package com.itqq.list; import org.junit.Test; import java.util.LinkedList; import java.util.Stack; public class TestStack { @Test public void test1(){ Stack<Integer> list = new Stack<>(); list.push(1); list.push(2); list.push(3); System.out.println("list = " + list); System.out.println("list.peek()=" + list.peek()); System.out.println("list.peek()=" + list.peek()); System.out.println("list.peek()=" + list.peek()); /* System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException */ while(!list.empty()){ System.out.println("list.pop() =" + list.pop()); } } @Test public void test2(){ LinkedList<Integer> list = new LinkedList<>(); list.push(1); list.push(2); list.push(3); System.out.println("list = " + list); System.out.println("list.peek()=" + list.peek()); System.out.println("list.peek()=" + list.peek()); System.out.println("list.peek()=" + list.peek()); /* System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop()); System.out.println("list.pop() =" + list.pop());//java.util.NoSuchElementException */ while(!list.isEmpty()){ System.out.println("list.pop() =" + list.pop()); } } }
13.7 队列
队列(Queue)是一种(但并非一定)先进先出(FIFO)的结构。
队列是逻辑结构,其物理结构可以是数组,也可以是链表。队列有普通队列、双端队列、并发队列等等,核心类库中的队列实现类有很多(后面会学到很多),LinkedList是双端队列的实现类。
Queue除了基本的 Collection
操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null
或 false
,具体取决于操作)。Queue
实现通常不允许插入 元素,尽管某些实现(如 )并不禁止插入 。即使在允许 null 的实现中,也不应该将 插入到 中,因为 也用作 方法的一个特殊返回值,表明队列不包含元素。
操作 | 方法1 | 方法2 |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove() | poll() |
检查 | element() | peek() |
Deque,名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。是Queue的子类,此接口定义在双端队列两端访问元素的方法。提供插入、移除和检查元素的方法。每种方法都存在两种形式:一种形式在操作失败时抛出异常,另一种形式返回一个特殊值(null
或 false
,具体取决于操作)。LinkedList是Deque接口的实现类,底层使用双向链表实现。
第一个元素(头部) | 最后一个元素(尾部) | |||
---|---|---|---|---|
插入 | addFirst(e) | offerFirst(e) | addLast(e) | offerLast(e) |
移除 | removeFirst() | pollFirst() | removeLast() | pollLast() |
检查 | getFirst() | peekFirst() | getLast() | peekLast() |
结论:Deque接口的实现类既可以用作FILO堆栈使用,又可以用作FIFO队列使用。
package com.itqq.queue; import java.util.LinkedList; public class TestQueue { public static void main(String[] args) { LinkedList<String> list = new LinkedList<>(); list.addLast("张三"); list.addLast("李四"); list.addLast("王五"); list.addLast("赵六"); while (!list.isEmpty()){ System.out.println("list.removeFirst()=" + list.removeFirst()); } } }
13.8 Map
13.8.1 概述
现实生活中,我们常会看到这样的一种集合:IP地址与主机名,身份证号与个人,系统用户名与系统用户对象等,这种一一对应的关系,就叫做映射。Java提供了专门的集合类用来存放这种对象关系的对象,即java.util.Map<K,V>
接口。
我们通过查看Map
接口描述,发现Map<K,V>
接口下的集合与Collection<E>
接口下的集合,它们存储数据的形式不同。
-
Collection
中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。 -
Map
中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。 -
Collection
中的集合称为单列集合,Map
中的集合称为双列集合。 -
需要注意的是,
Map
中的集合不能包含重复的键,值可以重复;每个键只能对应一个值(这个值可以是单个值,也可以是个数组或集合值)。
13.8.2 Map常用方法
1、添加操作
-
V put(K key,V value)
-
void putAll(Map<? extends K,? extends V> m)
2、删除
-
void clear()
-
V remove(Object key)
3、元素查询的操作
-
V get(Object key)
-
boolean containsKey(Object key)
-
boolean containsValue(Object value)
-
boolean isEmpty()
4、元视图操作的方法:
-
Set<K> keySet()
-
Collection<V> values()
-
Set<Map.Entry<K,V>> entrySet()
5、其他方法
-
int size()
import java.util.HashMap; public class TestMapMethod { public static void main(String[] args) { //创建 map对象 HashMap<String, String> map = new HashMap<String, String>(); //添加元素到集合 map.put("小好", "小谷"); map.put("钥匙", "锁"); map.put("上联", "下联"); map.put("许仙", "白蛇"); System.out.println(map); //String remove(String key) System.out.println(map.remove("小好")); System.out.println(map); // 想要查看 黄晓明的媳妇 是谁 System.out.println(map.get("钥匙")); System.out.println(map.get("上联")); } }
tips:
使用put方法时,若指定的键(key)在集合中没有,则没有这个键对应的值,返回null,并把指定的键值添加到集合中;
若指定的键(key)在集合中存在,则返回值为集合中键对应的值(该值为替换前的值),并把指定键所对应的值,替换成指定的新值。
13.8.3 Map集合的遍历
Collection集合的遍历:(1)foreach(2)通过Iterator对象遍历
Map的遍历,不能支持foreach,因为Map接口没有继承java.lang.Iterable<T>接口,也没有实现Iterator iterator()方法。只能用如下方式遍历:
(1)分开遍历:
-
单独遍历所有key
-
单独遍历所有value
(2)成对遍历:
-
遍历的是映射关系Map.Entry类型的对象,Map.Entry是Map接口的内部接口。每一种Map内部有自己的Map.Entry的实现类。在Map中存储数据,实际上是将Key---->value的数据存储在Map.Entry接口的实例中,再在Map集合中插入Map.Entry的实例化对象,如图示:
package com.itqq.map; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; public class TestMapIterate { public static void main(String[] args) { HashMap<String,String> map = new HashMap<>(); map.put("许仙", "白娘子"); map.put("董永", "七仙女"); map.put("牛郎", "织女"); map.put("许仙", "小青"); System.out.println("所有的key:"); Set<String> keySet = map.keySet(); for (String key : keySet) { System.out.println(key); } System.out.println("所有的value:"); Collection<String> values = map.values(); for (String value : values) { System.out.println(value); } System.out.println("所有的映射关系"); Set<Map.Entry<String,String>> entrySet = map.entrySet(); for (Map.Entry<String,String> entry : entrySet) { // System.out.println(entry); System.out.println(entry.getKey()+"->"+entry.getValue()); } } }
13.8.4 Map的实现类们
Map接口的常用实现类:HashMap、Hashtable、TreeMap、LinkedHashMap和Properties。其中HashMap是 Map 接口使用频率最高的实现类。
1、HashMap和Hashtable
HashMap和Hashtable都是哈希表。HashMap和Hashtable判断两个 key 相等的标准是:两个 key 的hashCode 值相等,并且 equals() 方法也返回 true。因此,为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
-
Hashtable是线程安全的,任何非 null 对象都可以用作键或值。
-
HashMap是线程不安全的,并允许使用 null 值和 null 键。
示例代码:添加员工姓名为key,薪资为value
package com.itqq.map; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Set; public class TestHashMap { public void test01(){ HashMap<String,Double> map = new HashMap<>(); map.put("张三", 10000.0); //key相同,新的value会覆盖原来的value //因为String重写了hashCode和equals方法 map.put("张三", 12000.0); map.put("李四", 14000.0); //HashMap支持key和value为null值 String name = null; Double salary = null; map.put(name, salary); Set<Map.Entry<String, Double>> entrySet = map.entrySet(); for (Map.Entry<String, Double> entry : entrySet) { System.out.println(entry); } } public void test02(){ Hashtable<String,Double> map = new Hashtable<>(); map.put("张三", 10000.0); //key相同,新的value会覆盖原来的value //因为String重写了hashCode和equals方法 map.put("张三", 12000.0); map.put("李四", 14000.0); //Hashtable不支持key和value为null值 /*String name = null; Double salary = null; map.put(name, salary);*/ Set<Map.Entry<String, Double>> entrySet = map.entrySet(); for (Map.Entry<String, Double> entry : entrySet) { System.out.println(entry); } } }
2、LinkedHashMap
LinkedHashMap 是 HashMap 的子类。此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。
示例代码:添加员工姓名为key,薪资为value
package com.itqq.map; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class TestLinkedHashMap { public static void main(String[] args) { LinkedHashMap<String,Double> map = new LinkedHashMap<>(); map.put("张三", 10000.0); //key相同,新的value会覆盖原来的value //因为String重写了hashCode和equals方法 map.put("张三", 12000.0); map.put("李四", 14000.0); //HashMap支持key和value为null值 String name = null; Double salary = null; map.put(name, salary); Set<Map.Entry<String, Double>> entrySet = map.entrySet(); for (Map.Entry<String, Double> entry : entrySet) { System.out.println(entry); } } }
3、TreeMap
基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
代码示例:添加员工姓名为key,薪资为value
package com.v.map; import java.util.Comparator; import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; import org.junit.Test; public class TestTreeMap { @Test public void test1() { TreeMap<String,Integer> map = new TreeMap<>(); map.put("Jack", 11000); map.put("Alice", 12000); map.put("zhangsan", 13000); map.put("haogu", 14000); map.put("Lucy", 15000); //String实现了Comparable接口,默认按照Unicode编码值排序 Set<Entry<String, Integer>> entrySet = map.entrySet(); for (Entry<String, Integer> entry : entrySet) { System.out.println(entry); } } @Test public void test2() { //指定定制比较器Comparator,按照Unicode编码值排序,但是忽略大小写 TreeMap<String,Integer> map = new TreeMap<>(new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareToIgnoreCase(o2); } }); map.put("Jack", 11000); map.put("Alice", 12000); map.put("zhangsan", 13000); map.put("haogu", 14000); map.put("Lucy", 15000); Set<Entry<String, Integer>> entrySet = map.entrySet(); for (Entry<String, Integer> entry : entrySet) { System.out.println(entry); } } }
4、Properties
Properties 类是 Hashtable 的子类,Properties 可保存在流中或从流中加载。属性列表中每个键及其对应值都是一个字符串。
存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法。
代码示例:
package com.itqq.map; import org.junit.Test; import java.util.Properties; public class TestProperties { @Test public void test01() { Properties properties = System.getProperties(); String fileEncoding = properties.getProperty("file.encoding");//当前源文件字符编码 System.out.println("fileEncoding = " + fileEncoding); } @Test public void test02() { Properties properties = new Properties(); properties.setProperty("user","lee"); properties.setProperty("password","123456"); System.out.println(properties); } }
13.8.5 Set集合与Map集合的关系
Set的内部实现其实是一个Map。即HashSet的内部实现是一个HashMap,TreeSet的内部实现是一个TreeMap,LinkedHashSet的内部实现是一个LinkedHashMap。
部分源代码摘要:
HashSet源码:
public HashSet() { map = new HashMap<>(); } public HashSet(Collection<? extends E> c) { map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } public HashSet(int initialCapacity, float loadFactor) { map = new HashMap<>(initialCapacity, loadFactor); } public HashSet(int initialCapacity) { map = new HashMap<>(initialCapacity); } //这个构造器是给子类LinkedHashSet调用的 HashSet(int initialCapacity, float loadFactor, boolean dummy) { map = new LinkedHashMap<>(initialCapacity, loadFactor); }
LinkedHashSet源码:
public LinkedHashSet(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true);//调用HashSet的某个构造器 } public LinkedHashSet(int initialCapacity) { super(initialCapacity, .75f, true);//调用HashSet的某个构造器 } public LinkedHashSet() { super(16, .75f, true); } public LinkedHashSet(Collection<? extends E> c) { super(Math.max(2*c.size(), 11), .75f, true);//调用HashSet的某个构造器 addAll(c); }
TreeSet源码:
public TreeSet() { this(new TreeMap<E,Object>()); } public TreeSet(Comparator<? super E> comparator) { this(new TreeMap<>(comparator)); } public TreeSet(Collection<? extends E> c) { this(); addAll(c); } public TreeSet(SortedSet<E> s) { this(s.comparator()); addAll(s); }
但是,咱们存到Set中只有一个元素,又是怎么变成(key,value)的呢?
以HashSet中的源码为例:
private static final Object PRESENT = new Object(); public boolean add(E e) { return map.put(e, PRESENT)==null; } public Iterator<E> iterator() { return map.keySet().iterator(); }
原来是,把添加到Set中的元素作为内部实现map的key,然后用一个常量对象PRESENT对象,作为value。
这是因为Set的元素不可重复和Map的key不可重复有相同特点。Map有一个方法keySet()可以返回所有key。
13.9 集合框架图
1.Collection
1、添加元素
2、删除和替换元素
3、获取元素
-
List(有序/允许重复/下标)
-
ArrayList :底层是数组 集合中的每个元素都有对应顺序索引 线程不安全的
增加了:关于下标的方法
1、添加元素
2、删除和替换元素
3、获取元素
4、获取元素索引
-
Vector:具备以上特性 线程安全的
-
LinkedList:底层是链表 添加和删除元素时,操作效率高
-
-
Set(无序/不允许重复)
-
HashSet:无序/不允许重复的集合
-
LinkHashSet:默认按照添加的顺序进行存储 通过链表记录添加的顺序
-
TreeSet:默认按照集合本身元素的类型的顺序进行排序,也可以传入Comparator接口的排序规则进行排序
List:有序 有下标 可以取出某个元素
set:不允许重复(去重) 查找集合元素使用:是否包含 contains
-
-
Queue:队列
-
Deque:允许前后来操作元素及取出元素
-
2.Map(双列集合/键值对映射)
-
HashMap:哈希表 (数组+链表存储方法 哈希函数) 不保证元素的顺序 线程不安全
1、添加操作
2、删除
3、元素查询的操作
4、元视图操作的方法
5、其他方法
-
HashTable:哈希表 (数组+链表存储方法 哈希函数) 线程安全
-
LinkedHashMap:是HashMap的子类,增加了链表的结果 可以维护Key和value的顺序
-
TreeMap:红黑树 可以实现key的排序 默认按照自然顺序或者根据传入的Comparator来决定排序顺序
使用场景:
HashMap:数据库查询结果 网络传入的数据
TreeMap:顺序排序的场景 有序存储和输出 区间查找
-
Properties:可保存流中数据或从流中加载 键和值是字符串
setProperty(String key,String value)方法
getProperty(String key)方法