一、集合框架的概述
集合、数组都是多个数据进行存储操作的结构,简称Java容器。
说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt、.jpg、.avi,数据库中)数组在存储多个数据方面的特点:
① 一旦初始化以后,其长度就确定了。
②数组一旦定义好,其元素类型也就确定了。我们也就只能操作指定类型的数据了
比如:String[] arr、int[] arr、Object[] arr等数组在存储多个数据方面的缺点:
① 一旦初始化以后,其长度就不可以修改了
② 数组提供的方法非常有限,对于添加、删除、插入等操作非常不便,同时效率不高
③ 获取数组中实际元素的得到个数需求,数组没有现成的属性或方法可用
④ 数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。
集合框架:
- Collection接口:单列集合,用来存储一个一个的对象
① List接口(Collection子接口):存储有序、可重复的数据(动态数组)
实现类:ArrayList、LinkedList、Vector
② Set接口(Collection子接口):存储无序、不可重复的数据(集合)
实现类:HashSet、LinkHashSet、TreeSet - Map接口:双列数据,用来存储一对(Key - Value)一对数据(高中函数:y = f(x))
实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、properties
二、Collection
1. Collection接口中声明的方法的测试
- add(Object e):奖元素添加到集合coll中
- addAll():将coll1集合的元素添加到当前元素的集合中
- size():获取添加元素的个数
- clear():清空集合元素
- isEmpty():判断集合是否为空
- contains(Object e):判断当前集合是否包含e
- containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
- remove(Object e):从当前集合中移除e元素
- removeAll(Collection coll1):从当前集合中移除coll1中的所有元素(差集)
- retainAll(Collection coll1):获取当前集合的coll1集合的交集,并返回给当前集合(交集)
- equals(Object e):判断当前集合和形参集合的元素是否相等(顺序也得一样)
- hashCode():返回当前对象的哈希值
- toArray():集合 --> 数组
- iterator():返回Iterator接口的实例,用于遍历集合元素
- 拓展:数组 --> 集合:调用Arrays类的静态方法asList()
测试代码:
public class CollectionTest {
@Test
public void test1(){
Collection coll = new ArrayList();
//1. add(Object e):奖元素添加到集合coll中
coll.add("AA");
coll.add("BB");
coll.add(123); //自动装箱
coll.add(new Date());
//2. size():获取添加元素的个数
System.out.println(coll.size());
//3. addAll():将coll1集合的元素添加到当前元素的集合中
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("abc");
coll.addAll(coll1);
System.out.println(coll.size());
System.out.println(coll);
//4. clear():清空集合元素
coll.clear();
//5. isEmpty():判断集合是否为空
System.out.println(coll.isEmpty());
//6. contains(Object e):判断当前集合是否包含e
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
System.out.println(coll.contains(123)); //true
System.out.println(coll.contains(new String("Tom"))); //true
System.out.println(coll.contains(new Person("Jerry", 20))); //false --> true
//7. containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中
Collection coll2 = Arrays.asList(123, 4546);
System.out.println(coll.containsAll(coll2));
//8. remove(Object e):从当前集合中移除e元素
coll.remove(123);
System.out.println(coll);
//9. removeAll(Collection coll1):从当前集合中移除coll1中的所有元素(差集)
Collection coll3 = Arrays.asList(456, false, new Person("Jerry", 20));
coll.removeAll(coll3);
System.out.println(coll);
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
//10. retainAll(Collection coll1):获取当前集合的coll1集合的交集,并返回给当前集合(交集)
Collection coll1 = Arrays.asList(123, 456);
coll.retainAll(coll1);
System.out.println(coll);
//11. equals(Object e):判断当前集合和形参集合的元素是否相等(顺序也得一样)
Collection coll2 = Arrays.asList("abc", 123);
Collection coll3 = Arrays.asList("abc", 123);
Collection coll4 = Arrays.asList(123, "abc");
//可以说明ArrayList是有序的
System.out.println(coll2.equals(coll3)); //true
System.out.println(coll2.equals(coll4)); //false
}
@Test
public void test3(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
//12. hashCode():返回当前对象的哈希值
System.out.println(coll.hashCode());
//13. toArray():集合 --> 数组
Object[] arr = coll.toArray();
System.out.println(Arrays.toString(arr));
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
//拓展:数组 --> 集合:调用Arrays类的静态方法asList()
List list = Arrays.asList(new int[]{123, 456});
System.out.println(list.size()); //1
List list1 = Arrays.asList(new Integer[]{123, 456});
System.out.println(list1.size()); //2
List<String> list2 = Arrays.asList(new String[]{"aa", "bb", "cc"});
System.out.println(list2);
//14. iterator():返回Iterator接口的实例,用于遍历集合元素
}
}
2. 集合的遍历操作
1. 使用Iterator接口(迭代器)
- 内部方法:
① hashNext():判断是否还有下一个元素
② next()指针下移,并将下移后集合位置上的元素返回
③ remove():可以在遍历的时候,删除集合中的元素
测试代码:
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
@Test
public void test2(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
Iterator iterator = coll.iterator();
while (iterator.hasNext()){
Object obj = iterator.next();
if("Tom".equals(obj)){
iterator.remove();
}
}
iterator = coll.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
2. 使用 foreach 循环
- Java 5.0 提供了 foreach 循环迭代访问 Collection和数组。
- 遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
- 遍历集合的底层调用Iterator完成操作。
- foreach还可以用来遍历数组。
for(集合元素的类型 局部变量 : 集合对象)
测试代码:
// 遍历集合
@Test
public void test1(){
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(false);
coll.add(new String("Tom"));
coll.add(new Person("Jerry", 20));
//for(集合元素的类型 局部变量 : 集合对象)
//内部任然可以调用迭代器
for(Object obj : coll){
System.out.println(obj);
}
}
//遍历数组
@Test
public void test2(){
int[] arr = new int[]{1, 2, 3, 4, 5};
for(int i : arr){
System.out.println(i);
}
}
3. Collection子接口之一:List接口
List接口(Collection子接口):存储有序、可重复的数据(动态数组)
实现类:ArrayList、LinkedList、VectorList接口常用的方法:
① 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位置的元素为ele
⑧ List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合总结:常用方法
增: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循环、 ③ 普通循环
测试代码:
@Test
public void test1(){
ArrayList list = new ArrayList();
list.add(123);
list.add(456);
list.add("AA");
list.add(new Person("Tom", 22));
list.add(456);
System.out.println(list);
//1. void add(int index, Object ele): 在index位置插入ele元素
list.add(1, "BB");
System.out.println(list);
//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
//3. Object get(int index): 获取指定index位置的元素
System.out.println(list.get(4));
//4. int indexOf(Object obj): 返回obj在集合中首次出现的位置(找不到,返回-1)
System.out.println(list.indexOf(456));
//5. int lastIndexOf(Object obj): 返回obj在当前集合中末次出现的位置
System.out.println(list.lastIndexOf(456));
//6. Object remove(int index): 移除指定index位置的元素,并返回此元素
System.out.println(list.remove(3));
System.out.println(list);
//7. Object set(int index, Object ele): 设置指定index位置的元素为ele
list.set(1, "CC");
System.out.println(list);
//8. List subList(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合(区间:[fromIndex, toIndex),即左闭右开)
List subList = list.subList(0, 2);
System.out.println(subList);
//方式一:Iterator迭代器方式
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//方式二:增强for循环
for(Object obj : list){
System.out.println(obj);
}
//方式三:普通for循环
for(int i = 0; i < list.size(); i++){
System.out.println(list.get(i));
}
}
1. List实现类之一:ArrayList
- ArrayList 是 List 接口的典型实现类、主要实现类
- 本质上,ArrayList是对象引用的一个”变长”数组
- ArrayList的JDK1.8之前与之后的实现区别?
- JDK1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
- JDK1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
2. List实现类之二:LinkedList
对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
新增方法:
void addFirst(Object obj)
void addLast(Object obj)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()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;
}
}
3. List 实现类之三:Vector
- Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList
相同,区别之处在于Vector是线程安全的。 - 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
- 新增方法:
void addElement(Object obj)
void insertElementAt(Object obj,int index)
void setElementAt(Object obj,int index)
void removeElement(Object obj)
void removeAllElements()
4. Collection子接口之二:Set接口
- Set接口(Collection子接口):存储无序、不可重复的数据(集合)
实现类:HashSet、LinkedHashSet、TreeSet
HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet
TreeSet:可以按照添加对象的指定属性,进行排序 - set接口没有额外的定义新的方法,使用的都是Collection中声明的方法
- 要求1:向Set中添加的数据,其所在类一定要重写hashCode()、equals()
要求2:重写的hashCode()和equals()尽可能保持一致性(即相等的对象必须具有相等的散列码)
1. Set实现类之一:HashSet
- HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
- HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
- HashSet 具有以下特点:
- 不能保证元素的排列顺序
- HashSet 不是线程安全的
- 集合元素可以是 null
- HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相等,并且两个对象的 equals() 方法返回值也相等。
- 对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“相等的对象必须具有相等的散列码”。
测试代码:
@Test
public void test1(){
Set set = new HashSet();
set.add(123);
set.add(456);
set.add("AA");
set.add(new Person("Tom", 22));
set.add(new Person("Tom", 22));
set.add(true);
set.add("BB");
set.add(123);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
2. 添加元素的过程(以HashSet为例)
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过底层某种算法计算出在HashSet底层数组中存放的位置(即为:索引位置),判断数组此位置上是否已经有元素:
- 如果此位置上没有其它元素,则元素a添加成功 —> 情况1
- 如果此位置上有其他元素b(或以链表形式存在多个元素),则比较元素a和b的hash值:
- 如果hash值不相同,则元素a添加成功。 —> 情况2
- 如果hash值相同,进而需要调用元素a所在类的equals()方法:
- equals()返回true,元素a添加失败
- equals()返回false,元素a添加成功 —> 情况3
对于添加成功的情况2和情况3而言:元素a于已经存在指定索引位置上的数据以链表方式存储。
- jdk 7:元素a放到数组中,指向原来的元素
- jdk 8:原来的元素放在数组中,指向元素a
HashSet底层:数组 + 链表的结构
3. Set实现类之二:LinkedHashSet
- LinkedHashSet 是 HashSet 的子类
- LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。
- LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。
- LinkedHashSet 不允许集合元素重复。
- LinkedHashSet底层结构
优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
测试代码:
/**
* LinkedHashSet的使用:
* LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据嗨维护了两个引用,记录此数据前一个数据和后一个数据。
* 优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet
*/
@Test
public void test2(){
Set set = new LinkedHashSet();
set.add(123);
set.add(456);
set.add("AA");
set.add(new Person("Tom", 22));
set.add(new Person("Tom", 22));
set.add(true);
set.add("BB");
set.add(123);
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
4. Set实现类之三:TreeSet
- 向TreeSet中添加的数据,要求是相同类的对象。
- 两种排序方式:自然排序(s实现了Comparable接口) 和 定制排序
- 自然排序和定制排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不在是equals()。
测试代码:
/**
* TreeSet的使用:
* 1. 向TreeSet中添加的数据,要求是相同类的对象。
* 2. 两种排序方式:自然排序(s实现了Comparable接口) 和 定制排序
* 3. 自然排序和定制排序中,比较两个对象是否相同的标准为:CompareTo()返回0,不在是equals()。
*/
@Test
public void test3(){
TreeSet set = new TreeSet();
// 失败:不能添加不同类的对象
// set.add(123);
// set.add(456);
// set.add("AA");
// set.add(new Person("Tom", 22));
// 举例一:
// set.add(123);
// set.add(-12);
// set.add(34);
// set.add(45);
// 举例二:(自然排序)
set.add(new Person("Tom", 23));
set.add(new Person("Marry", 13));
set.add(new Person("John", 20));
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 50));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
@Test
public void test4(){
//定制排序
Comparator comparator = new Comparator() {
//按照年龄从小到大排序
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof Person && o2 instanceof Person){
Person person1 = (Person) o1;
Person person2 = (Person) o2;
return Integer.compare(person1.getAge(), person2.getAge());
}
throw new RuntimeException("输入的数据不匹配");
}
};
TreeSet set = new TreeSet(comparator);
set.add(new Person("Tom", 23));
set.add(new Person("Marry", 13));
set.add(new Person("John", 20));
set.add(new Person("Jack", 30));
set.add(new Person("Jack", 50));
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
三、Map接口
1. Map实现类的结构情况
- Map:双列数据,存储key - value对的数据(类似于高中的函数:y = f(x))
HashMap:作为Map的主要实现类;线程不安全,效率高;能存储null的key和value
- LinkedHashMap:保证遍历map元素时,可以按照的顺序实现遍历
原因:在原有的HashMap底层结构的基础上,添加了一对指针,指向前一个和后一个元素(双向链表)
- LinkedHashMap:保证遍历map元素时,可以按照的顺序实现遍历
TreeMap:保证按照添加的key - value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序
底层使用的是时红黑树Hashtable:作为古老的实现类;线程安全,效率低;不能存储null的key和value
- Properties:常用来处理配置文件。key和value都是String类型
HashMap的底层:数组 + 链表(jdk7及以前)
数组 + 链表 + 红黑树(jdk8)
2. Map结构的理解
- Map中的key:无序的、不可重复的、使用Set存储所有的key —> key所在的类要重写equals()和hashCode()(以HashMap为例)
- Map中的value:无序的、可重复的、使用Collection存储所有的value --> value所在类要重写equals()
- 一个键值对:key-value构成了一个Entry对象
- Map中的entry:无序的、不可重复的、使用Set存储所有的entry
3. Map接口:常用方法
- 添加、删除、修改操作:
① Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
② void putAll(Map m):将m中的所有key-value对存放到当前map中
③ Object remove(Object key):移除指定key的key-value对,并返回value
④ void clear():清空当前map中的所有数据 - 元素查询的操作:
① Object get(Object key):获取指定key对应的value
② boolean containsKey(Object key):是否包含指定的key
③ 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集合
常用方法总结:
添加:put(Object key,Object value)
删除:remove(Object key)
修改:put(Object key,Object value)
查询:get(Object key)
长度:size()
遍历:keySet()、values()、entrySet()
测试代码:
public class MapTest {
@Test
public void test1(){
Map map = new HashMap();
map.put(123, "AA");
map.put(234, "BB");
map.put(465, "CC");
System.out.println(map);
}
@Test
public void test2(){
Map map = new LinkedHashMap();
map.put(123, "AA");
map.put(234, "BB");
map.put(465, "CC");
System.out.println(map);
}
@Test
public void test3(){
Map map = new HashMap();
//添加
map.put("AA", 123);
map.put(45, 123);
map.put("BB", 56);
//修改
map.put("AA", 87);
System.out.println(map);
Map map1 = new HashMap();
map1.put("CC", 123);
map1.put("DD", 123);
map.putAll(map1);
System.out.println(map);
//remove(Object key)
System.out.println(map.remove("CC"));
System.out.println(map);
//clear()
map.clear();
System.out.println(map);
}
@Test
public void test4(){
Map map = new HashMap();
map.put("AA", 123);
map.put(45, 123);
map.put("BB", 456);
//get(Object key)
System.out.println(map.get("AA"));
//containsKey(Object key)
System.out.println(map.containsKey("BB"));
//containsValue(Object value)
System.out.println(map.containsValue(123));
//size()
System.out.println(map.size());
//isEmpty()
// map.clear();
System.out.println(map.isEmpty());
//equals(Object obj)
System.out.println(map.equals("BB"));
}
@Test
public void test5(){
Map map = new HashMap();
map.put("AA", 123);
map.put(45, 123);
map.put("BB", 456);
//Set kySet:遍历所有的key集
Set set = map.keySet();
System.out.println(set);
//Collection values():
Collection collection = map.values();
System.out.println(collection);
//Set entrySet(),集合中的元素都是entry
Set set1 = map.entrySet();
System.out.println(set1);
}
}
4. HashMap的底层实现原理?以jdk7为例说明
- HashMap map = new HashMap():
在实例化以后,底层创建了、长度为16的一维数组Entry[] table - map.put(key1, value1):
首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置
如果此位置上的数据为空,此时的key1-value1添加成功。— 情况1
如果此位置上的数据不为空,(此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经在一个或多个数据的哈希值:
- 如果key1的哈希值和已经存在的数据的哈希值都不相同,此时key1-value1添加成功。 — 情况2
- 如果key1的哈希值和已经存在某一个数据的哈希值相同,继续比较,调用key1所在类的equals()方法,比较:
- 如果equals()返回false:此时key1-value1添加成功。 — 情况3
- 如果equals()返回true:使用value1替换相同key的value值。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断添加过程中,会涉及到扩容问题,默认扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
jdk8相较于jdk7在底层实现方面的不同:
- new HashMap():底层没有创建一长度为16的数组
- jdk8底层的数组是:Node[],而非Entry[]
- 首次调用put()方法时,底层创建长度为16的数组
- jdk7底层结构只有:数组 + 链表。jdk8中底层的结构:数组 + 链表 + 红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8且当前数组长度 > 64时,此时此索引位置上的所有数据改为使用红黑树存储。
5. LinkedHashMap的底层实现原理
- LinkedHashMap 是 HashMap 的子类
- 在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序
- 与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致
- 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中的内部类:Entr
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. Map实现类之三:TreeMap
- TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于有序状态。
- TreeSet底层使用红黑树结构存储数据
- TreeMap 的 Key 的排序:
- 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException
- 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现Comparable 接口
- TreeMap判断两个key相等的标准:两个key通过compareTo()方法或者compare()方法返回0。
测试代码:
package Map;
import org.junit.Test;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* @author XiaoQ
* @create 2022-10-04 21:58
*/
public class TreeMapTest {
//向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要求按照key进行排序:自然排序、定制排序
//自然排序
@Test
public void test1(){
Map map = new TreeMap();
map.put(new User("aaa", 12), new User("asf", 23));
map.put(new User("sdf", 12), new User("dfg", 13));
map.put(new User("acv", 12), new User("dfg", 45));
map.put(new User("dfh", 42), new User("ert", 22));
map.put(new User("aaa", 11), new User("ghj", 9));
Set set = map.entrySet();
for(Object obj : set){
System.out.println(obj);
}
/*
输出:
User{name='sdf', age=12}=User{name='dfg', age=13}
User{name='dfh', age=42}=User{name='ert', age=22}
User{name='acv', age=12}=User{name='dfg', age=45}
User{name='aaa', age=11}=User{name='ghj', age=9}
User{name='aaa', age=12}=User{name='asf', age=23}
总结:如果User类的CompareTo()返回非0,则这两个User类不相等,返回0,则相等
*/
}
//定制排序
@Test
public void test2(){
Map map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
User user = (User) o1;
User user1 = (User) o2;
if(user instanceof User && user1 instanceof User){
return user.getName().compareTo(user1.getName());
}
throw new RuntimeException("输入数据不一致");
}
});
map.put(new User("aaa", 12), new User("asf", 23));
map.put(new User("sdf", 12), new User("dfg", 13));
map.put(new User("acv", 12), new User("dfg", 45));
map.put(new User("dfh", 42), new User("ert", 22));
map.put(new User("aaa", 11), new User("ghj", 9));
Set set = map.entrySet();
for(Object obj : set){
System.out.println(obj);
}
}
@Test
public void test3(){
TreeMap treeMap = new TreeMap();
treeMap.put(1, 2);
treeMap.put(1, 3);
treeMap.put(4, 2);
treeMap.put(2, 2);
Set set = treeMap.entrySet();
for(Object obj : set){
System.out.println(obj);
}
}
}
7. Map实现类之四:Hashtable
- Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。
- Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。
- 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value
- 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序
- Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。
8. Map实现类之五:Properties
- Properties 类是 Hashtable 的子类,该对象用于处理属性文件
- 由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key 和 value 都是字符串类型
- 存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
测试代码:
package Map;
import org.junit.Test;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
/**
* @author XiaoQ
* @create 2022-10-04 22:53
*/
public class PropertiesTest {
//Properties:常用来处理配置文件,key和value都是String类型
@Test
public void test1(){
Properties properties = new Properties();
try {
FileInputStream fis = new FileInputStream("jdbc.properties");
properties.load(fis); //加载流对应文件
String name = properties.getProperty("name");
String password = properties.getProperty("password");
System.out.println("name: " + name);
System.out.println("password: " + password);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
四、Collections工具类
*Collections:操作Collection、Map的工具类
- 排序操作:(均为static方法)
① 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):返回指定集合中指定元素的出现次数
⑥ void copy(List dest,List src):将src中的内容复制到dest中
⑦ boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
测试代码:
@Test
public void test1(){
List list = new ArrayList();
list.add(123);
list.add(34);
list.add(334);
list.add(456);
list.add(-45);
System.out.println(list);
//reverse(List)
Collections.reverse(list);
System.out.println(list);
//shuffle(List)
Collections.shuffle(list);
System.out.println(list);
//sort(List)
Collections.sort(list);
System.out.println(list);
//swap(List, int, int)
Collections.swap(list, 1, 2);
System.out.println(list);
//int frequency(Collection,Object)
System.out.println(Collections.frequency(list, 456));
//oid copy(List dest,List src)
//报异常: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);
System.out.println(list);
}
- Collections类提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并访问集合时的线程安全问题
@Test
public void test2(){
List list = new ArrayList();
list.add(123);
list.add(34);
list.add(334);
list.add(456);
list.add(-45);
List list1 = Collections.synchronizedList(list); //返回的list1即为线程安全的List
}