[toc]
一、Java集合框架概述
1.1 集合框架的概述
- 面向对象语言对事物的体现都以对象的形式,为了方便对多个对象操作,就要对对象进行存储
- 使用 Array 存储对象方面具有 一些弊端,而 Java 集合就像一种容器,可以动态的把多个对象的引用放入容器
- 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt, .jpg , .avi, 数据库中)
1.1.1 数组的特点
- 一旦初始化以后,其长度就确定了。
- 数组一旦定义好,其元素的类型也就确定了。我们只能操作指定类型的数据了。
- 比如: String[] arr; int[] arr1; Object[] arr2;
1.1.2 数组的缺点
- 一旦初始化之后,其长度不可修改。
- 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便。
- 获取数组中是实际元素的个数需求,数组没有现成的属性和方法可用
- 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
1.2 Java集合的两种体系
1.2.1 Collection 接口
-
单列数据,定义了存取一组对象的方法的集合
-
List:元素有序、可重复的集合。“动态“数组
- ArrayList 、LinkedList 、Vector
-
Set:元素无序、不可重复的集合。 高中讲的”集合“
- HashSet、 LinkedHashSet、TreeSet
-
List:元素有序、可重复的集合。“动态“数组
-
Collection 接口继承树(实线是继承关系,虚线是实现关系)
image-20201022143539482
1.2.2 Map 接口(y=f(x))
HasMap、LinkedHashMap、TreeMap、Hashtable、Properties
双列数据,保存具有映射关系“key-value对”的集合
-
Map接口继承树(实线是继承,虚线是实现)
image-20201022143746725
二、Collection接口方法
- 向Collection 接口实现类的对象中添加数据obj时,要求obj所在类要重写equals().
2.1 add(Object e)
-
将元素e添加到集合中中
Collection coll = new ArrayList(); coll.add("AA"); coll.add(123); //自动装箱 coll.add(new Date);
2.2 addAll(Collection coll1)
-
将集合 coll1 中的元素添加到集合中
Collection coll = new ArrayList(); Collection coll1 = new ArrayList(); coll1.add(456); coll1.add("CC"); coll.add(coll1);
2.3 size():return int;
-
获取添加元素的个数(默认值:0)
List arr1 = Arrays.asList(new int[]{123, 456}); System.out.println(arr1.size()); //1 List arr2 = Arrays.asList(new Integer[]{123, 456}); System.out.println(arr2.size()); //2
2.4 clear()
-
清空集合的元素
coll1.clear();
2.5 isEmpty()
-
判断当前集合是否为空
System.out.println(coll1.isEmpty());
2.6 contains(Object obj)
判断当前集合中是否包含obj
-
在判断的时候会调用obj对象所在类的equals()
Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20)); System.out.println(coll.contains(new String("Tom"))); //true System.out.println(coll.contains("Tom")); //true System.out.println(coll.contains(new Person("Jerry",20) ));//false Person p = new Person("Jerry",20); System.out.println(coll.contains(p)); //false
-
重写equals() 后
System.out.println(coll.contains(new Person("Jerry",20) ));//true Person p = new Person("Jerry",20); System.out.println(coll.contains(p)); //true
2.7 containsAll(Connection coll)
-
判断形参coll 中所有元素是否都存在于当前集合中。
Collection coll1 = Arrays.asList(123,456); System.out.println(coll.containsAll(coll1));
2.8 remove(Object obj)
-
从当前集合中移除obj元素
System.out.println(coll.remove(123));//true System.out.println(coll.remove(new Person("Jerry",20))); //false
-
重写equals方法后
System.out.println(coll.remove(new Person("Jerry",20))); //true
2.9 removeAll(Collection coll)
-
从当前集合中移除coll中所有的元素(差集: 移除两个集合共有)
Collection coll1 = Arrays.asList(123,4567); coll.removeAll(coll1);
2.10 retainAll(Collection coll1)
-
交集:获取当前集合和coll1集合的交集,并返回给当前集合
Collection coll1 = Arrays.asList(123,4567); coll.retainAll(coll1); System.out.println(coll); //[123]
2.11 equals(Object obj)
要想返回true,需要当前集合和形参集合的元素都相同
另外就是看右边的对象,有序,还是**
-
如下是ArrayList
Collection coll = new ArrayList(); coll.add(123); coll.add(456); coll.add(new String("Tom")); coll.add(false); coll.add(new Person("Jerry",20));
Collection coll1 = new ArrayList(); coll1.add(456); coll1.add(123); coll1.add(false); coll1.add(new String("Tom")); coll1.add(new Person("Jerry",20));
System.out.println(coll.equals(coll1)); //false
2.12 hasCode()
-
返回当前对象的哈希值
System.out.println(coll.hashCode());
2.13 toArray
-
将集合--> 数组
Object[] arr = coll.toArray(); for (Object o : arr) { System.out.println(o); }
-
将数组--->集合
- 调用Arrays类的静态方法asList()
List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
2.14 iterator()
- 返回Iterator 接口的实例,用于遍历集合元素。
三、Iterator 迭代器接口
3.1迭代器
- Iterator对象称为迭代器,主要用于遍历 Collection集合中的元素。
- 集合对象每次调用iterator() 都得到一个全新的迭代器对象
- Iterator 不是容器
3.2 迭代器模式(GOF定义)
- 提供一种方法访问一个容器(container) 对象中各个元素,而又不需要暴露该对象的内部细节。迭代器模式,就是为容器而生。
3.3 next()
指针下移
-
将下移以后的集合位置上的元素返回
Iterator iterator = coll.iterator(); System.out.println(iterator.next());
3.4 hasNext()
-
判断是否还有下一个元素
while(iterator.hasNext()){ System.out.println(iterator.next()); }
3.4 remove()
可以在遍历的时候,删除集合中的元素,此方法不同于集合直接调用remove()
-
如果未调用next() 或在上一次调用 next 方法之后已经调用了 remove 方法,再调用 remove 都会报IllegalStateException
Iterator iterator = coll.iterator(); while(iterator.hasNext()){ Object obj = iterator.next(); if(obj.equals("Tom")){ iterator.remove(); } }
3.5 迭代器的执行原理
3.6 迭代器遍历的两种错误方式
//错误方式一:
Iterator iterator1 = coll.iterator();
while((iterator1.next())!=null){
System.out.println(iterator1.next());
}
//错误方式二:每次调用iterator() 都得到一个全新的迭代器对象。默认游标都在集合的第一个元素之上
while(coll.iterator().hasNext()){
System.out.println(coll.iterator().next());
}
3.7 使用foreach 循环遍历集合元素
JDK 5.0 提供了 foreach 循环迭代访问 Collection和数组。
遍历操作不需获取 Collection 或 数组的长度,无需使用索引访问元素
遍历集合的底层调用 Iterator 完成操作
foreach 可以用来遍历数组
内部仍然调用了迭代器
-
for(集合元素类型 局部变量 : 集合对象){}
for (Object o : coll) { System.out.println(o); }
-
遍历数组 for(数组元素的类型 局部变量 : 数组对象)
int[] arr = new int[]{1,2,3,4,5}; for (int i : arr) { System.out.println(i); }
四、Collection 子接口一:List
4.1List接口概述
- 鉴于Java中数组用来存储数据的局限性,所以通常使用List替换数组
- List集合类中 元素有序、且可重复,集合中每个元素都有其对应的顺序索引
- List容器中的元素都对应一个整数型的序号,可以根据序号存取容器的元素
- JDK API 中 List接口的实现类 常用的有:ArrayList 、LinkedList 和 Vector
4.2 ArrayList、LinkedList、Vector 三者的异同
相同:
* 三个类都**实现了 List 接口**,**存储数据的特点**相同:**存储有序、可重复的数据**
不同:
- ArrayList 作为 List 接口的主要实现类,线程不安全的,执行效率高,底层使用Object[]存储
- Vector 作为 List 接口的古老实现类,线程安全的,效率低,底层使用Object[]存储
- LinkedList 对于频繁的插入和删除操作,使用此类效率比ArrayList高 ,底层使用双向链表
4.3 ArrayList 的源码分析
4.3.1 JDK 7 情况下
ArrayList list = new ArrayList();
-
底层默认创建了长度是10 的 Object[] 数组elementData
- list.add(123);//elementData[0] = new Integer(123);
- ……
- list.add(1) ; 如果此次的添加导致底层 elementData 数组容量不够,则扩容。
- 默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
- 结论:建议开发中使用带参的构造器: ArrayList list = new ArrayList(int capacity);
4.3.2 JDK 8 中的变化
ArrayList list = new ArrayList();
- 底层Object[] elementData 初始化为{} ,并没有创建长度为10的数组
- list.add(123); //第一次调用add() 时,底层才创建了长度10的数组,并将数据123 添加到elementData[0];
- 后续的添加扩容与jdk 7.0 无异。
4.3.3 小结
- JDK 7 中的 ArrayList 的对象的创建类似于单例 的 饿汉式
- JDK 8 中的ArrayList 的对象的创建类似于单例 的 懒汉式 ,延迟了数组的创建,节省了内存。
4.4 LinkedList 的底层实现源码
- prev 变量记录前一个元素的位置
- next 变量记录下一个元素的位置
- 体现了 LinkedList 的双向链表的说法
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;
}
}
4.4.1源码分析
LinkedList list = new LinkedList();
- 内部声明了 Node 类型的first 和 last 属性,默认值为 null
list.add(123);
- 将 123 封装到Node中,创建了 Node 对象
4.5 Vector
- 线程安全
- 通过 Vector() 构造器创建对象时,底层都创建了长度为10 的数组
- 在扩容方面,默认扩容为原来的数组长度的2倍
4.6 List 接口的常用方法
4.6.1 void add(int index,Object ele)
-
在 index 位置插入 ele 元素
List list = new ArrayList(); list.add(123); list.add(456); list.add(new String("Tom")); list.add(false); list.add(new Person("Jerry",20)); System.out.println(list); //[123, 456, Tom, false, Person{name='Jerry', age=20}] //void add list.add(1,"BB"); //[123, BB, 456, Tom, false, Person{name='Jerry', age=20}] System.out.println(list);
4.6.2 boolean addAll(int index,Collection eles)
-
从 index 位置开始将 eles 中的所有元素添加进来
List list1 = Arrays.asList(1,2,3); list.addAll(list1); System.out.println(list.size()); //9
4.6.3 Object get(int index)
-
获取指定 index 位置的元素
System.out.println(list.get(0)); //123
4.6.4 int indexOf(Object obj)
返回obj在集合中首次出现的位置
-
若不存在返回-1
int index = list.indexOf(456); System.out.println(index); //1
4.6.5 int lastIndexOf(Object obj)
-
返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf(false)); //3
4.6.6 Object remove(int index)
-
移除指定 index 位置的元素,并返回此元素
Object obj = list.remove(0); System.out.println(obj); //123 System.out.println(list); //[456, Tom, false, Person{name='Jerry', age=20}]
4.6.7 Object set(int index,Object ele)
-
设置指定 index 位置的元素为 ele
list.set(1,"CC"); System.out.println(list); //[456, CC, false, Person{name='Jerry', age=20}]
4.6.8 List subList(int fromIndex,int toIndex)
-
返回从fromIndex 到 toIndex 位置的子集合
List list1 = list.subList(2, 4); System.out.println(list1); //[false, Person{name='Jerry', age=20}]
4.6.9 常用方法总结
方法 | |
---|---|
增 | add(Object obj) |
删 | remove(int index) /remove(Object obj) |
改 | set(int index,Object ele) |
查 | get(int index) |
插 | add(int index,Object ele) |
长度 | size() |
遍历 | ① Iterator 迭代器遍历 ② 增强for 循环 ③普通 的循环 |
4.6.10 List 的三种遍历方式
① Iterator 迭代器遍历
Iterator iterator = list.iterator();
while(iterator.hasNext(){
System.out.println(iterator.next());
}
② 增强for 循环
for (Object o : list) {
System.out.println(o);
}
③ 普通的循环
//方式三: for
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
五、Collection 子接口二:Set
5.1 Set 接口的概述
- 存储无序的、不可重复的数据
- Set 接口中没有额外定义新的方法,使用的都是Collection 中声明过的方法
5.1.1 HashSet
- 作为 Set 接口 的主要实现类:
- 线程不安全的,可以存储 null 值
- 底层:数组+链表的结构
- 对应的类一定要重写 equals() 和 hashCode(Object obj) 方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”
5.1.2 LinkedHashSet
- 作为 HashSet 的子类,遍历其内部数据时,可以按照添加的顺序遍历
5.1.3 TreeSet
- 可以按照添加对象的指定属性进行排序
5.2 Set 的特性分析
5.2.1无序性
-
不等于随机性。
Set set = new HashSet(); set.add(123); set.add("AA"); set.add(new String("CC")); set.add(new Person("Tom",21)); Iterator iterator = set.iterator(); while(iterator.hasNext()){ /* AA CC 123 Person{name='Tom', age=21} */ System.out.println(iterator.next()); }
存储的数据在底层数组中并非按照数组索引的顺序添加,而是由数据的哈希值决定的
5.2.2 不可重复性
-
保证添加的元素按照 equals() 判断时,不能返回 true,即:相同的元素只能添加一个
Set set = new HashSet(); set.add(123); set.add(123); set.add("AA"); set.add(new String("CC")); set.add(new String("CC")); set.add(new Person("Tom",21)); set.add(new Person("Tom",21)); Iterator iterator = set.iterator(); while(iterator.hasNext()) /* AA CC Person{name='Tom', age=21} 123 Person{name='Tom', age=21} */ System.out.println(iterator.next()); }
5.3 添加元素的过程:(HashSet 为例)
-
向 HashSet 中添加元素 a ,首先调用元素a所在类的 hashCode() 方法,计算 a 的哈希值,此哈希值通过某种算法计算出在 HashSet 底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
情况一:如果此位置没有其他元素 则元素 a 添加成功。
-
如果此位置有其他元素 b (或以链表形式存在的多个元素),则比较元素 a 与 元素 b 的 hash 值:
- 情况二:如果 hash 值不相同,则元素 a 添加成功
- 如果 hash 值相同,进而需要调用元素 a 所在类的 equals() 方法:
- equals() 返回 true,元素 a 添加失败
- 情况三:equals() 返回 false,则元素 a 添加成功。
-
对于添加成功的情况二和情况三而言:元素 a 与已经存在指定索引位置上数据以链表的方式存储。
- JDK 7:元素 a 放在数组中,指向原来的元素
- JDK 8:原来的数组在数组总,指向元素 a
- 总结:七上八下
image-20201023103627894
5.4 关于 hashCode 和 equals 的重写
- 向Set 中添加的数据,其所在的类一定要重写 hashCode() 和 equals()
- 对应的类一定要重写 equals() 和 hashCode(Object obj) 方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”
- 重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field, 都应该用来计算 hashCode 值
5.5 LinkedHashSet
5.5.1 底层结构
5.5.2LinkedHashSet 的使用
- 作为 HashSet 的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
- 优点:对于频繁的遍历操作,LinkedHashSet 效率高于 HashSet
5.6 TreeSet
5.6.1 要求
- 向TreeSet 中添加的数据,要求是相同类的对象
- 两种排序方式:自然排序(实现Compare接口) 和 定制排序
- 自然排序中,比较两个对象是否相同的标准为:compareTo() 返回0,不在是equals()
5.6.2底层原理(红黑树)
5.6.3 自然排序
-
自然排序中,比较两个对象是否相同的标准为:compareTo() 返回0,不在是equals()
public class Person implements Comparable{ private String name; private int age; /** * 按照姓名从大到小排列,年龄从小到大排列 * @param o * @return */ @Override public int compareTo(Object o) { if(o instanceof Person){ Person person = (Person)o; int compare = -this.name.compareTo(person.name); if(compare != 0){ return compare; }else{ return Integer.compare(this.age,person.age); } }else{ throw new RuntimeException("输入的类型不匹配"); } } }
5.6.4 定制排序
-
定制排序中,比较两个对象是否相同的标准为:compare() 返回0,不在是equals()
Comparator com = new Comparator(){ /** * 按照年龄从小到大排序 * @param o1 * @param o2 * @return */ @Override public int compare(Object o1, Object o2) { if(o1 instanceof Person && o2 instanceof Person){ Person p1 = (Person)o1; Person p2 = (Person)o2; return Integer.compare(p1.getAge(), p2.getAge()); }else{ throw new RuntimeException("输入的数据类型不匹配"); } } };
六、Map接口
- 双列数据,存储 key-value 对的数据
6.1 Map接口继承树
6.2 Map的实现类的特点
1. HashMap:
- 作为Map 的主要实现类:线程不安全的,效率高
- 存储null的key 和 value
-
底层:数组+链表 (jdk 7 及之前)
- 数组 + 链表 + 红黑树 (jdk 8)
1.1 LinkedHashMap:
- 保证在遍历 Map 元素时,可以按照添加的顺序实现遍历
- 在原有的 HashMap 底层结构基础上,添加了一对指针,指向前一个和后一个元素
- 对于频繁的遍历操作,此类执行效率高于 HashMap
2. TreeMap:
- 保证按照添加的 key-value 对进行排序,实现排序遍历。
- 此时考虑 key 的自然排序 或 定制排序
- 底层使用的红黑树
3. Hashtable:
- 作为古老的实现类:线程安全的,效率低
- 不能存储 null 的 key 和 value
3.1 Properities:
- 常用来处理配置文件。
- key 和 value 都是 String 类型。
6.3 Map 结构的理解
- Map 中的 key :无序的、不可重复的,使用 Set 存储所有的key ---> key 所在的类要重写 equals() 和 hashCode() (以 HashMap 为例)
- Map 中的 value:无序的、可重复的,使用 Collection 存储所有的 value --> value 所在的类要重写 equals()
- 一个键值对:key-value 构成了一个 Entry 对象
- Map 中的 entry: 无序的、不可重复的,使用 Set 存储所有的 entry
6.4 HashMap 的底层实现原理
6.4.1 JDK 7
HashMap map = new HashMap();
-
在实例化以后,底层创建了长度是16的一维数组 Entry[] table.
……可能已经执行过多次put……
map.put(key1,value1);
首先,调用 key1 所在类的 hashCode() 计算 key1 哈希值,此哈希值经过某种算法计算后,得到 Entry数组中的存放位置。
如果此位置上的数据为空,此时key1-value1 添加成功。 --<font color=red>情况1</font>
-
如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据[以链表形式存在]),比较 key1 和已经存在的一个或多个数据的哈希值:
- 如果 key1 的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。--<font color=red>情况2</font>
- 如果 key1 的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法:
- 如果 equals() 返回false:此时 key1-value1 添加成功。 --<font color=red>情况3</font>
- 如果 equals() 返回true:使用 vlaue1 替换 value2。
补充:关于<font color=red>情况2</font> 和 <font color=red>情况3</font>: 此时 key1-value1 和原来的数据以链表的方式存储。
在不断添加的过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空时),默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
6.4.2 JDK 8
- new HashMap(): 底层没有创建一个长度为16的数组
- 底层的数组是: Node[] , 而非Entry[]
- 首次调用put() 方法时,底层创建长度为 16 的数组
-
底层结构: 数组 + 链表 + 红黑树
- 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且 当前数组的长度 > 64时,
- 此时此索引位置上的所有数据改为使用红黑树存储。
6.4.3 源码中的重要常量
常量 | |
---|---|
DEFAULT_INITAL_CAPACITY | HashMap 的默认容量,16 |
MAXIMUM_CAPACITY | HashMap 的最大支持容量,2^30 |
DEFAULT_LOAD_FACTOR | HashMap 的默认加载因子,0.75 |
TREEIFY_THRESHOLD | Bucket 中链表的长度大于该默认值,转化为红黑树 8 |
UNTREEIFY_THRESHOLD | Bucket 中红黑树存储的Node小于该默认值,转化为链表 |
MIN_TREEIFY_CAPACITY | 桶中的Node被树化最小的hash表容量。64 |
table | 存储元素的数组,总是 2 的 n 次幂 |
entrySet | 存储具体元素的集 |
size | HashMap 中存储的键值对的数量 |
modCount | HashMap 扩容和结构改变的次数 |
threshold | 扩容的临界值, = 容量*填充因子 16 * 0.75 = 12 |
loadFactor | 填充因子 |
6.5 LinkedHashMap 底层实现原理
源码中:
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); } }
6.6 Map 中定义的方法
6.6.1 Object put(Object key,Object value)
-
将指定 key-value 添加到(或修改)当前 map 对象
Map map = new HashMap(); map.put("AA", 123); map.put("AA", 87); map.put("BB", 56); map.put(45, 123);
6.6.2 void putAll(Map m)
-
将m中的所有 key-value 对存放到当前map中
Map map1 = new HashMap(); map1.put("CC", 123); map1.put("DD", 123); //{AA=87, BB=56, CC=123, DD=123, 45=123} map.putAll(map1);
6.6.3 Object remove(Object key)
-
移除指定key-value对,返回value
Object value = map.remove("CC"); System.out.println(map); //{AA=87, BB=56, DD=123, 45=123} System.out.println(value); //123
6.6.4 void clear()
-
清空当前 map 中的所有数据
map.clear(); //与map = null不同 System.out.println(map.size()); //0 System.out.println(map);//{}
6.6.5 Object get(Object key)
-
获取指定 key 对应的value
Map map = new HashMap(); map.put("AA", 123); map.put(45, 123); map.put("BB", 56); System.out.println(map.get(45));//123
6.6.6 boolean containsKey(Object key)
-
是否包含指定的key
boolean isExist = map.containsKey("BB"); System.out.println(isExist);//true
6.6.7 boolean containsValue(Object value)
-
是否包含指定的value
boolean b = map.containsValue(123); System.out.println(b);//true
6.6.8 int size()
-
返回 map 中 key-value 对的个数
System.out.println(map.size());//3
6.6.9 boolean isEmpty()
-
判断当前 map 是否为空
map.clear(); System.out.println(map.isEmpty());//true
6.6.10 boolean equals(Object obj)
判断当前 map 和参数对象 obj 是否相等
-
map 和 map1 中key-value都相同才为true
Map map1 = new HashMap(); map1.put("AA", 123); map1.put(45, 123); map1.put("BB", 56); //map 和 map1 中key-value都相同才为true System.out.println(map.equals(map1));
6.6.11 Set keySet()
-
返回所有 key 构成的 Set 集合
Map map = new HashMap(); map.put("AA", 123); map.put(45, 123); map.put("BB", 56); Set set = map.keySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); }
6.6.12 Collection values()
-
返回所有 value 构成的 Collection 集合
Collection values = map.values(); for (Object value : values) { System.out.println(value); }
6.6.13 Set entrySet
-
返回所有 key-value 对构成的 Set 集合
Set set1 = map.entrySet(); Iterator iterator1 = set1.iterator(); while(iterator1.hasNext()){ Object obj = iterator1.next(); Map.Entry entry = (Map.Entry) obj; System.out.println(entry.getKey() + " " + entry.getValue()); }
-
key-value 的遍历方式二:
Set keySet = map.keySet(); Iterator iterator2 = keySet.iterator(); while(iterator2.hasNext()){ Object key = iterator2.next(); Object value = map.get(key); System.out.println(key + "=" + value ); }
6.7 TreeMap 的两种添加方式的使用
向 TreeMap 中添加 key-value ,要求 key 必须是由同一类创建的对象
因为要按照 key 进行排序: 自然排序 、 定制排序
6.7.1 自然排序
-
User类 实现 Comparable 接口
TreeMap map = new TreeMap(); User u1 = new User("Tom", 23); User u2 = new User("Jerry", 32); User u3 = new User("Jack", 20); User u4 = new User("Rose", 18); map.put(u1, 98); map.put(u2, 89); map.put(u3, 76); map.put(u4, 100); Set set = map.entrySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ Map.Entry entry =(Map.Entry) (iterator.next()); System.out.println(entry.getKey() + "==" + entry.getValue()); }
6.7.2 定义排序
-
在创建 new TreeMap () 的时候重写Comparator 方法
TreeMap map = new TreeMap(new Comparator() { @Override public int compare(Object o1, Object o2) { if(o1 instanceof User && o2 instanceof User){ User u1 = (User)o1; User u2 = (User)o2; return Integer.compare(u1.getAge(), u2.getAge()); } throw new RuntimeException("输入的类型不匹配"); } }); User u1 = new User("Tom", 23); User u2 = new User("Jerry", 32); User u3 = new User("Jack", 20); User u4 = new User("Rose", 18); map.put(u1, 98); map.put(u2, 89); map.put(u3, 76); map.put(u4, 100); Set set = map.entrySet(); Iterator iterator = set.iterator(); while(iterator.hasNext()){ Map.Entry entry =(Map.Entry) (iterator.next()); System.out.println(entry.getKey() + "==" + entry.getValue()); }
6.8 Properties
-
Properties中内容
name=Tom路飞 password=123456
-
读取Properties 的代码
FileInputStream fis = null; try { Properties pros = new Properties(); fis = new FileInputStream("jdbc.properties"); pros.load(fis); //加载对应的流文件 String name = pros.getProperty("name"); String password = pros.getProperty("password"); System.out.println(name + " " +password); } catch (IOException e) { e.printStackTrace(); } finally { try { if(fis != null){ fis.close(); } } catch (IOException e) { e.printStackTrace(); } }
如果读取中文出现乱码,修改如下配置:
七、Collections工具类
- Collections 是一个操作 Set 、List 和 Map 等集合的工具类
7.1 面试题:Collection 和 Collections 的区别?
- Collections 是操作 Collection 和 map 的工具
- Collection 是单列数据的接口,其中常见的接口List 和 Set
7.2 排序操作(均为静态)
7.2.1 reverse(List)
-
反转 List 中元素的顺序
Collections.reverse(list);
7.2.2 shuffle(List)
-
对 List 集合元素进行随机排序
Collections.shuffle(list);
7.2.3 sort(List)
-
根据元素的自然顺序对指定 List 结合元素按升序排序
Collections.sort(list);
7.2.4 sort(List,Comparator)
- 根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
7.2.5 swap(List,int,int)
-
将指定的 list 结合中 i 元素 和 j 处元素进行交换
Collections.swap(list, 1, 2);
7.3 查找、替换
7.3.1 Object max(Collection)
- 根据元素的自然顺序,返回给定集合中的最大元素
7.3.2 Object max(Collection,Comparator)
- 根据Comparator 指定的顺序,返回给定集合中的最大元素
7.3.3 Object min(Collection)
- 根据元素的自然顺序,返回给定集合中的最小元素
7.3.4 Object min(Collection,Comparator)
- 根据Comparator 指定的顺序,返回给定集合中的最小元素
7.3.5 int frequency(Collection, Object)
-
返回指定集合中指定元素的出现次数
int frequency = Collections.frequency(list, 65); System.out.println(frequency);
7.3.6 void copy(List dest,List src)
将 src 中内容复制到 dest 中
-
错误的添加方法(会报异常)
//java.lang.IndexOutOfBoundsException: Source does not fit in dest // List dest = new ArrayList(); // Collections.copy(dest,list);
-
正确的添加方法
List dest = Arrays.asList(new Object[list.size()]); Collections.copy(dest, list); System.out.println(dest);
使用新值替换 List 对象的所有旧值
7.4 同步控制
-
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
List list1 = Collections.synchronizedList(list); //返回线程安全的list