1.Set集合
-
- Set概述
java.util.Set 接口继承了 Collection 接口,是常用的一种集合类型。相对于之前学习的List集合,Set集合特点如下:
除了具有 Collection 集合的特点,还具有自己的一些特点:
Set是一种无序的集合
Set是一种不带下标索引的集合Set是一种不能存放重复数据的集合
重点学习的Set实现类:
HashSet 底层借助哈希表实现TreeSet 底层借助二叉树实现
注意,TreeSet是Set接口的子接口SortedSet的实现类
基础案例:
实例化一个Set集合,往里面添加元素并输出,注意观察集合特点(无序、不重复)
import java.util.HashSet; import java.util.Iterator; import java.util.Set;
public class Test071_SetBasic {
public static void main(String[] args) {
//1.实例化Set集合,指向HashSet实现类对象Set<String> set = new HashSet<>();
set.add("hello1"); set.add("hello2"); set.add("hello3"); set.add("hello4");
set.add("hello5"); //添加失败 重复元素set.add("hello5"); //添加失败 重复元素
//加强for循环遍历
for(String obj : set){ System.out.println(obj);
}
System.out.println("-----------------");
//迭代器遍历
Iterator<String> it = set.iterator(); while(it.hasNext()){
Object obj = it.next(); System.out.println(obj);
}
}
}
//输出结果:hello1 hello4 hello5 hello2 hello3
-----------------
通过以上代码和运行结果,可以看出Set类型集合的特点:无序、不可重复
HashSet
java.util.HashSet 是Set接口的实现类,它使用哈希表(Hash Table)作为其底层数据结构来存储数据。
HashSet特点:
无序性:HashSet中的元素的存储顺序与插入顺序无关
HashSet使用哈希表来存储数据,哈希表根据元素的哈希值来确定元素的存储位置,而哈希值是根据元素的内容计算得到的,与插入顺序无关。
唯一性:HashSet中不允许重复的元素,即每个元素都是唯一的
允许null元素:HashSet允许存储null元素,但只能存储一个null元素,HashSet中不允许重复元素
高效性:HashSet的插入、删除和查找操作的时间复杂度都是O(1)
哈希表通过将元素的哈希值映射到数组的索引来实现快速的插入、删除和查找操作。
基础案例1:
实例化HashSet对象,往里面插入多个字符串,验证HashSet特点。
package com.briup.chap08.test;
import java.util.HashSet; import java.util.Set;
public class Test072_Basic {
public static void main(String[] args) {
//1.实例化HashSet对象
Set<String> set = new HashSet<>();
//2.往集合中添加元素set.add("hello"); set.add("world"); set.add("nihao"); set.add("hello"); set.add(null); set.add(null);
System.out.println("size: " + set.size());
//3.遍历
for (String str : set) { System.out.println(str);
}
}
}
//运行结果:size: 4 null
world
nihao
hello
根据结果可知,HashSet无序、唯一、null值可存在。
元素插入过程:
当向HashSet中插入元素时,先获取元素的hashCode()值,再检查HashSet中是否存在相同哈希值的元素,如果元素哈希值唯一,则直接插入元素
如果存在相同哈希值的元素,会调用元素的equals()方法来进一步判断元素是否是相同。如果相同,则不会插入重复元素;如果不同,则插入
方法复习:
hashCode() 方法是 Object类 中的一个方法,它返回一个 int 类型的哈希码值
通常情况下, hashCode() 方法会根据对象的属性值计算一个哈希码值(重写自定义类中 hashCode方法 )
如果两个对象的hashCode值不同,则两个对象的属性值肯定不同
如果两个对象的hashCode值相同,则两个对象的属性值可能相同,也可能不同
结论:如果要往HashSet集合存储自定义类对象,那么一定要重写自定义类中的hashCode方法和equals方法。
TreeSet
TreeSet是SortedSet(Set接口的子接口)的实现类,它基于红黑树(Red- Black Tree)数据结构实现。
TreeSet特点:
有序性:插入的元素会自动排序,每次插入、删除或查询操作后,TreeSet会自动调整元素的顺序,以保持有序性。
唯一性:TreeSet中不允许重复的元素,即集合中的元素是唯一的。如果尝试插入一个已经存在的元素,该操作将被忽略。
动态性:TreeSet是一个动态集合,可以根据需要随时添加、删除和修改元素。插入和删除操作的时间复杂度为O(log n),其中n是集合中的元素数量。
高效性:由于TreeSet底层采用红黑树数据结构,它具有快速的查找和插入性能。对于有序集合的操作,TreeSet通常比HashSet更高效。
入门案例1:
准备一个TreeSet集合,放入多个Integer元素,观察是否自动进行排序。
public class Test073_Integer {
public static void main(String[] args) {
//1.实例化集合对象
Set<Integer> set = new TreeSet<>();
//2.添加元素set.add(3); set.add(5); set.add(1); set.add(7);
//3.遍历
for(Integer obj : set) { System.out.println(obj);
}
}
}
//输出结果:
1
3
5
7
问题分析:
根据异常提示可知错误原因是: Person 无法转化成 Comparable ,从而导致ClassCastException异常 。
为什么会这样呢?
TreeSet是一个有序集合,存储数据时,一定要指定元素的排序规则,有两种指定的方式,具体如下:
TreeSet排序规则:自然排序(元素所属类型要实现 java.lang.Comparable 接口)比较器排序上述2个案例中,Integer存如TreeSet没有报错,是因为Integer类已经实现自然排序,而Person类既没有实现自然排序,也没有额外指定比较器排序规则。
1)TreeSet:自然排序
如果一个类,实现了 java.lang.Comparable 接口,并重写了 compareTo方法 ,那么这个类的对象就是可以比较大小的。
public interface Comparable<T> { public int compareTo(T o);
}
compareTo方法说明:
int result = this.属性.compareTo(o.属性);
result的值大于0,说明this比o大result的值小于0,说明this比o小result的值等于0,说明this和o相等
元素插入过程:
当向TreeSet插入元素时,TreeSet会使用元素的 compareTo() 方法来比较元素之间的大小关系。根据比较结果,TreeSet会将元素插入到合适的位置,以保持有序性。如果两个元素相等( compareTo() 方法返回0),TreeSet会认为这是重复的元素,只会保留一个。
案例展示:
使用自然排序(先按name升序,name相同则按age降序)解决上述案例问题。
基础Person类:
package com.briup.chap08.bean;
//定义Person类,实现自然排序
public class Person implements Comparable<Person> { private String name;
private int age;
public Person() {}
public Person(String name, int age) { this.name = name;
this.age = age; }
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public int getAge() { return age;
}
public void setAge(int age) {
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]"; }
//重写方法,指定比较规则:先按name升序,name相同则按age降序
@Override
public int compareTo(Person o) {
//注意:字符串比较需要使用compareTo方法
int r = name.compareTo(o.name); if(r == 0) {
r = o.age - this.age; }
return r; } }
测试类:
package com.briup.chap08.test;
import java.util.Set; import java.util.TreeSet;
import com.briup.chap08.bean.Person;
//自然排序测试
public class Test073_Comparable {
public static void main(String[] args) {
//1.实例化TreeSet
Set<Person> set = new TreeSet<>();
//2.添加元素
set.add(new Person("zs",21)); set.add(new Person("ww",20)); set.add(new Person("zs",21)); set.add(new Person("tom",19)); set.add(new Person("tom",23)); set.add(new Person("jack",20));
//3.遍历集合
for (Person person : set) { System.out.println(person);
}
}
//输出结果:
Person [name=jack, age=20]
Person [name=tom, age=23]
Person [name=tom, age=19]
Person [name=ww, age=20]
Person [name=zs, age=21]
补充内容1:Integer类中重写compare源码
可以看出,Integer的俩个对象比较大小,其实就是比较Integer对象对应的int值的大小
注意,compareTo方法返回值代表的含义(三种情况:正数、负数、零)补充内容2:整形、浮点型、字符串自然排序规则
对于整型、浮点型元素,它们会按照从小到大的顺序进行排序。对于字符串类型的元素,它们会按照字典顺序进行排序。
注意事项:compareTo方法的放回结果,只关心正数、负数、零,不关心具体的值是多少
2)TreeSet:比较器排序
思考:如果上述案例中Person类不是自定义类,而是第三方提供好的(不可以修改源码),那么如何实现排序规则的指定?
我们可以可以使用比较器(Comparator)来自定义排序规则。
比较器排序步骤:
创建一个实现了Comparator接口的比较器类,并重写其中的 compare() 方法
该方法定义了元素之间的比较规则。在 compare() 方法中,我们可以根据元素的属性进行比较,并返回负整数、零或正整数,来表示元素之间的大小关系。
创建TreeSet对象时,将比较器对象作为参数传递给构造函数,这样,TreeSet会根据比较器来进行排序。
TreeSet类构造器补充:
//实例化TreeSet类对象时,需要额外传入一个比较器类对象
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator)); }
jcompare方法说明:
int result = compare(o1, o2);
result的值大于0,表示升序result的值小于0,表示降序
result的值等于0,表示元素相等,不能插入
注意,这里和自然排序的规则是一样的,只关心正数、负数、零,不关心具体的值是多少
案例说明:
使用比较器排序,对上述案例中Person进行排序,要求先按age升序,age相同则按name降序。
基础类需要注释掉自然排序接口:
//定义Person类,实现自然排序
public class Person /* implements Comparable<Person> */ { private String name;
private int age;
public Person() {}
public Person(String name, int age) { this.name = name;
this.age = age; }
public String getName() { return name;
}
public void setName(String name) { this.name = name;
}
public int getAge() { return age;
}
public void setAge(int age) { this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]"; }
//重写方法,指定比较规则
// @Override
// public int compareTo(Person o) { } }
//先按name升序,name相同则按age降序
int r = name.compareTo(o.name);
if(r == 0) {
r = o.age - this.age; }
return r;测试类:
package com.briup.chap08.test;
import java.util.Comparator; import java.util.Set;
import java.util.TreeSet;
import com.briup.chap08.bean.Person;
//比较器排序测试
public class Test073_Comparator {
public static void main(String[] args) {
//1.准备自定义比较器对象
// 匿名内部类形式
Comparator<Person> comp = new Comparator<Person>() {
//重写比较算法:先按按age升序,age相同则按name降序
@Override
public int compare(Person o1, Person o2) {
int r = o1.getAge() - o2.getAge(); if(r == 0) {
//注意:字符串比较需要使用compareTo方法
r = o2.getName().compareTo(o1.getName());
}
return r;
};
//2.实例化TreeSet,传入自定义比较器对象
Set<Person> set = new TreeSet<>(comp);
//3.添加元素
set.add(new Person("zs",21)); set.add(new Person("ww",20)); set.add(new Person("zs",21)); set.add(new Person("tom",19)); set.add(new Person("tom",23)); set.add(new Person("jack",20));
//4.遍历集合
for (Person person : set) { System.out.println(person);
} }
//输出结果:
Person [name=tom, age=19]
Person [name=ww, age=20]
Person [name=jack, age=20]
Person [name=zs, age=21]
Person [name=tom, age=23]
注意:如果同时使用自然排序和比较器排序,比较器排序将覆盖自然排序。
2.自然排序:
使用元素类实现Comparable接口,并重写其中的 compareTo() 方法元素会按照其自身的比较规则进行排序
自然排序是默认的排序方式,可以直接使用TreeSet或Collections.sort()方法进行排序
只能有一种自然排序方式比较器排序:
使用Comparator对象来定义元素之间的比较规则
可以自定义排序规则,不依赖于元素类的实现Comparable接口
需要创建一个实现Comparator接口的比较器类,并重写其中的 compare()方法
可以有多个比较器,根据需要选择不同的比较器进行排序
可以通过传入Comparator对象给TreeSet或Collections.sort()方法来进行比较器排序
自然排序和比较器排序都有自己的应用场景。自然排序适用于元素类已经实现了Comparable接口,并且希望按照元素自身的比较规则进行排序的情况。比较器排序适用于需要自定义排序规则,或者元素类没有实现Comparable接口的情
况。
LinkedHashSet
LinkedHashSet 是 HashSet 的一个子类,具有 HashSet 的高效性能和唯一性特性,并且保持了元素的插入顺序,其底层基于哈希表和链表实现。
案例展示:
实例化一个LinkedHashSet集合对象,存入多个String字符串,观察是否唯一及顺序存储。
package com.briup.chap08.test;
import java.util.Iterator; import java.util.LinkedHashSet; import java.util.Set;
public class Test074_LinkedHashSet {
public static void main(String[] args) {
// 1.实例化LinkedHashSet
Set<String> set = new LinkedHashSet<>();
// 2.添加元素set.add("bbb"); set.add("aaa"); set.add("abc"); set.add("bbc"); set.add("abc");
// 3.迭代器遍历
Iterator<String> it = set.iterator(); while (it.hasNext()) {
System.out.println(it.next()); }
}
}
//运行结果:
bbb
aaa
abc
bbc
-
- Set小结
集合类 | 特点 | 示例 |
HashSet | 基于哈希表实现,无序集合,不允许重复元素。 | Set set = new HashSet<>(); |
TreeSet | 基于红黑树实现,有序集合,不允许重复元素。 | Set set = new TreeSet<>(); |
LinkedHashSet | 基于哈希表和链表实现,按插入顺序排序,不允许重复元素。 | Set set = new LinkedHashSet<> (); |
3.Map集合
很多时候,我们会遇到成对出现的数据,例如,姓名和电话,身份证和人,IP和域名等等,这种成对出现,并且一一对应的数据关系,叫做映射。java.util.Map<K, V> 接口,就是专门处理这种映射关系数据的集合类型。
Map集合是一种用于存储键值对(key-value)映射关系的集合类。它提供了一种快速查找和访问数据的方式,其中每个键都是唯一的,而值可以重复。
Map概述
Collection接口为单列集合的根接口,Map接口为双列集合的根接口。Map集合与Collection集合,存储数据的形式不同:
Map集合特点:
存储元素时,必须以key-value(键值对)的方式进行
键唯一性:Map集
合中的键是唯一的,每个键只能对应一个值
可重复值:Map集合中的值可以重复,不同的键可以关联相同的值
高效的查找和访问:通过给定键key值(唯一),可以快速获取与之对应的value值
Map集合内部使用哈希表或红黑树等数据结构来实现高效的查找和访问
Map接口常用方法(注意泛型K代表Key,范型V代表Value):
//把key-value存到当前Map集合中
V put(K key, V value)
//把指定map中的所有key-value,存到当前Map集合中
void putAll(Map<? extends K,? extends V> m)
//当前Map集合中是否包含指定的key值
boolean containsKey(Object key)
//当前Map集合中是否包含指定的value值
boolean containsValue(Object value)
//清空当前Map集合中的所有数据
void clear()
//在当前Map集合中,通过指定的key值,获取对应的value
V get(Object key)
//在当前Map集合中,移除指定key及其对应的value
V remove(Object key)
//返回当前Map集合中的元素个数(一对key-value,算一个元素数据)
int size()
//判断当前Map集合是否为空
boolean isEmpty()
//返回Map集合中所有的key值
Set<K> keySet()
//返回Map集合中所有的value值
Collection<V> values()
//把Map集合中的的key-value封装成Entry类型对象,再存放到set集合中,并返回
24 Set<Map.Entry<K,V>> entrySet()
Map集合实现类:
Java提供的Map集合实现类,常见的包括HashMap、TreeMap、LinkedHashMap等。它们在内部实现和性能方面有所不同,可以根据具体需求选择适合的实现类。
HashMap ,底层借助哈希表实现,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键所属类的hashCode()方法、equals()方法
(重要,最常用)
Hashtable :和之前List集合中的 Vector 的功能类似,可以在多线程环境中,保证集合中的数据的操作安全(线程安全)
LinkedHashMap :该类是 HashMap 的子类,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致(存入顺序就是取出顺序)
TreeMap :该类是 Map 接口的子接口 SortedMap 下面的实现类,和TreeSet 类似,它可以对key值进行排序,同时构造器也可以接收一个比较器对象作为参数。支持key值的自然排序和比较器排序俩种方式(支持key排序)
基本方法使用案例:
import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.Set;
public class Test081_MapBasic {
//双列集合 存放 id-name
public static void main(String[] args) {
//1.创建HashMap集合对象,并添加元素Map<Integer,String> map = new HashMap<>();
//注意,使用put方法添加键值对map.put(1, "zs"); map.put(2, "ls"); map.put(4, "rose"); map.put(3, "jack");
map.put(2, "lucy"); //lucy 会把 ls覆盖掉
//2.输出集合元素个数
//System.out.println(map); System.out.println("size: "+map.size());
//3.判断key和value是否存在
System.out.println("key 2: " + map.containsKey(2)); System.out.println("value ls: " +
map.containsValue("ls"));
//4.根据key获取value
String name = map.get(3); System.out.println("3: " + name);
//5.根据key删除键值对map.remove(3); System.out.println(map);
}
//运行结果:
size: 4
key 2: true value ls: false 3: jack
{1=zs, 2=lucy, 4=rose}
Map遍历
Map集合提供了2种遍历方式。
1)第一种遍历思路
借助Map中的keySet方法,获取一个Set集合对象,内部包含了Map集合中所有的key,进而遍历Set集合获取每一个key值,再根据key获取对应的value。
keySet遍历案例:
2)第二种遍历思路
借助Map中的entrySet方法,获取一个Set对象,内部包含了Map集合中所有的键值对,然后对键值对进行拆分,得到key和value进行输出。
Map 接口 entrySet() 方法解析:将 Map 集合中的每一组key-value(键值对)都封装成一个 Entry 类型对象,并且把这些个 Entry 对象存放到Set集合中,并返回。
entrySet遍历案例:对前面案例中的map集合对象进行遍历。import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.Map.Entry;public class Test082_Each {
//双列集合 存放 id-name
public static void main(String[] args) {
//1.创建HashMap集合对象,并添加元素
Map<Integer,String> map = new HashMap<>();
map.put(1, "zs"); map.put(2, "ls"); map.put(4, "rose"); map.put(3, "jack");
map.put(2, "lucy"); //lucy 会把 ls覆盖掉
//2.第二种遍历
// 获取所有的key-value键值对,得到一个Set集合
Set<Entry<Integer,String>> entrySet = map.entrySet();
// 遍历Set集合
for (Entry<Integer, String> entry : entrySet) {
// 拆分键值对中的key和value Integer key = entry.getKey(); String value = entry.getValue();
System.out.println("id: " + key + " name: " + value);
}
}
}
//运行结果:
id: 1 name: zs id: 2 name: lucy id: 3 name: jack id: 4 name: rose
HashMap
HashMap底层借助哈希表实现,元素的存取顺序不能保证一致。
HashMap存储的键值对时,如果键类型为自定义类,那么一般需要重写键所属类的hashCode()和equals()方法(重要,最常用)。
HashMap特点:
键唯一
值可重复
无序性
线程不安全
键和值的允许使用null【重点记忆】
/ 4.第一种遍历方法
Set<Student> keySet = map.keySet(); for (Student key : keySet) {
System.out.println(key + ": " + map.get(key)); }
System.out.println("---------------");
// 第二种方式遍历
Set<Entry<Student, String>> entrySet = map.entrySet(); for (Entry<Student, String> entry : entrySet) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
//运行结果:
size: 6
Student(ww,67)是否存在: true是否存在 009: false
remove(Student(lucy, 70)): 019
--------------- null: 002
Student [name=rose, age=82]: 005 Student [name=zs, age=78]: 010 Student [name=tom, age=86]: null Student [name=ww, age=67]: 002
---------------
省略...
结论:key类型如果为自定义类型,重写其hashCode和equals方法!
HashMap中add(key,value)时,需要判断key是否存在(先hashCode再equals)
HashMap中containsKey(key)时,同样要借助hashCode和equals方法HashMap中remove(key)时,同样要借助hashCode和equals方法
Hashtable
Hashtable是Java中早期的哈希表实现,它实现了Map接口,并提供了键值对的存储和访问功能。
Hashtable特点:
JDK1.0提供,接口方法较为复杂,后期实现了Map接口
线程安全:Hashtable是线程安全的,相对于HashMap性能稍低
键和值都不能为null:如果尝试使用null作为键或值,将会抛出NullPointerException
哈希表实现:和HashMap一样,内部使用哈希表数据结构来存储键值对
案例展示:
实例化Hashtable<Student, String>对象,添加元素并遍历。
package com.briup.chap08.test;
import java.util.Enumeration; import java.util.Hashtable;
import com.briup.chap08.bean.Student;
public class Test084_Hashtable {
public static void main(String[] args) {
// 1.实例化HashMap对象,其中key类型为自定义Student
Hashtable<Student, String> map = new Hashtable<>();
// 2.往集合中添加元素
// map中插入键值对,调用key所属类的hashCode和equals方法进行判断是否重复
map.put(new Student("zs", 78), "010"); map.put(new Student("rose", 82), "005"); map.put(new Student("lucy", 70), "009");
map.put(new Student("lucy", 70), "019"); // 相同key,只能保留一项,"019"会覆盖"009"
map.put(new Student("ww", 67), "002");
// 注意:Hashtable中key和value不能为null,否则抛出
NullPointerException
//map.put(new Student("tom", 86), null);
//map.put(null, "002");
// 3.遍历,Hashtable早期提供的方法较为繁琐Enumeration<Student> keys = map.keys(); while(keys.hasMoreElements()) {
Student key = keys.nextElement(); String value = map.get(key);
System.out.println(key + ": " + value); }
}
//运行输出:
Student [name=lucy, age=70]: 019 Student [name=zs, age=78]: 010 Student [name=ww, age=67]: 002 Student [name=rose, age=82]: 005
Hashtable小结:单线程环境下建议使用HashMap,性能更好
多线程环境下,建议使用 HashMap + Collections
TreeMap
TreeMap是有序映射实现类,它实现了SortedMap接口,基于红黑树数据结构来存储键值对。
TreeMap特点:
键的排序:TreeMap中的键是按照自然顺序或自定义比较器进行排序的
红黑树实现:TreeMap内部使用红黑树这种自平衡二叉搜索树数据结构来存储键值对
键唯一,值可重复
线程不安全,如果在多线程环境下使用TreeMap,应该使用Collections工具类处理
初始容量:TreeMap没有初始容量的概念,它会根据插入的键值对动态地调整红黑树的大小
TreeMap小结:
TreeMap底层借助红黑树实现,它提供了高效的有序映射功能,可以用于范围查找、排序和遍历等操作。但红黑树的平衡操作会带来额外的开销,相比于HashMap等实现类,TreeMap在插入和删除操作上可能稍慢。
因此,在选择使用TreeMap时,需要根据具体需求权衡性能和有序性的需求。
LinkedHashMap
LinkedHashMap是HashMap的一个子类,底层在哈希表的基础上,通过维护一个双向链表来保持键值对的有序性,可以保证存取次序一致。
基础案例:
创建LinkedHashMap<String, Student>对象,添加元素,观察是否存取次序一致。
import java.util.LinkedHashMap; import java.util.Map;
import com.briup.chap08.bean.Student;
// LinkedHashMap可以保证 存取次序一致
public class Test086_LinkedHashMap {
public static void main(String[] args) {
//1.实例化LinkedHashMap类对象
Map<String, Student> map = new LinkedHashMap<>();
//2.添加元素
map.put("010", new Student("zs", 78)); map.put("005", new Student("rose", 82)); map.put("009", new Student("lucy", 70));
map.put("019", new Student("lucy", 70)); //value可以重复
map.put("002", null);
map.put(null, new Student("ww", 67));
//3.遍历
for (String key : map.keySet()) { System.out.println(key + " " + map.get(key));
}
}
}
//结果输出:
010 Student [name=zs, age=78]
005 Student [name=rose, age=82]
009 Student [name=lucy, age=70]
019 Student [name=lucy, age=70]
002 null
null Student [name=ww, age=67]
可以看出,数据存入Map中的顺序,就是存储的顺序,也是取出的顺序!
Map小结
集合类 | 特点 | 示例 |
HashMap | 基于哈希表实现,无序键值对集合,键和值均可为null。 | Map<String, Integer> map = new HashMap<>(); |
TreeMap | 基于红黑树实现,按键有序排序,键不可为null。 | Map<String, Integer> map = new TreeMap<>(); |
LinkedHashMap | 基于哈希表和双向链表实现,按插入顺序排序,键和值均可为null。 | Map<String, Integer> map = new LinkedHashMap<> (); |
Hashtable | 基于哈希表实现,键值对存储,线程安全,键无序且不允许重复,健和值都不能为null。 | Map<String, Integer> map = new Hashtable<>(); |
Collections
数组工具类是 java.util.Arrays ,可以专门来操作数组对象,提供静态方法,可直接调用。
集合工具类是 java.util.Collections ,专门来操作集合对象,里面都是静态方法,可以直接调用。
注意, Collection 是单列集合根接口, Collections 是集合工具类,两者不同!
1) 常用方法:
package java.util;
public class Collections {
// 类外不能实例化对象,工具类主要调用static方法
private Collections() { }
//填充元素值
public static <T> void fill(List<? super T> list, T obj) {
//省略...
}
//获取最大值
public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) {
Iterator<? extends T> i = coll.iterator(); T candidate = i.next();
while (i.hasNext()) {
T next = i.next();
if (next.compareTo(candidate) > 0) candidate = next;
}
return candidate; }
//排序
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null); }
//省略 ... }
2)具体方法讲解
fill方法,使用指定元素替换指定列表中的所有元素
List<Integer> list = new ArrayList<>(); list.add(1);
list.add(2); list.add(3);
Collections.fill(list, 20);
for(Integer o : list) { System.out.println(o);
}
max方法,根据元素的自然顺序,返回给定集合的最大元素
List<Integer> list = new ArrayList<>(); list.add(1);
list.add(9); list.add(3);
System.out.println(Collections.max(list));
min方法,根据元素的自然顺序,返回给定集合的最小元素reverse方法,反转集合中的元素
List<Integer> list = new ArrayList<>(); list.add(1);
list.add(9); list.add(3);
Collections.reverse(list);
for(Integer o : list) { System.out.println(o);
}
sort方法,根据元素的自然顺序,对指定列表按升序进行排序
List<Integer> list = new ArrayList<>(); list.add(1);
list.add(9); list.add(3);
//如果需要,也可以在第二个参数位置传一个比较器对象
//Collections.sort(list,c); Collections.sort(list);
for(Integer o : list) { System.out.println(o);
}shuffle方法,使用默认随机源对指定列表进行置换
List<Integer> list = new ArrayList<>(); list.add(1);
list.add(9); list.add(3);
Collections.shuffle(list);
for(Integer o : list) { System.out.println(o);
}
addAll方法,往集合中添加一些元素
List<Integer> list = new ArrayList<>();
//注意,addAll的第二个参数,是可变参数Collections.addAll(list,1,3,5,7);
for(Integer o : list) { System.out.println(o);
}synchronized相关方法,将非线程安全的集合转为线程安全(了解,后续多线程再学习)
synchronizedCollection,把非线程安全的Collection类型集合,转为线程安全的集合
synchronizedList,把非线程安全的List类型集合,转为线程安全的集合synchronizedSet,把非线程安全的Set类型集合,转为线程安全的集合synchronizedMap,把非线程安全的Map类型集合,转为线程安全的集合
案例展示:
准备一个集合,往里面添加元素,然后使用工具类上述方法对其进行操作,观察效果。
package com.briup.chap08.test;
import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List;
public class Test09_Collections {
public static void main(String[] args) {
//1.准备集合并添加元素
//List<Integer> list = Arrays.asList(5,3,4,2,2,7); List<Integer> list = new ArrayList<>();
//addAll 往集合中添加多个元素Collections.addAll(list,5,3,4,2,2,7); System.out.println(list);
//2.max 获取最大值【默认采用自然排序】Integer max = Collections.max(list); System.out.println("max: " + max);
//3.min 获取集合最小值
Integer min = Collections.min(list); System.out.println("min: " + min);
//4.reverse 反转Collections.reverse(list); System.out.println(list);
//4.sort 排序【按照 传入的排序算法 进行排序】Comparator<Integer> comp = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
// 逆序
return o2-o1;
}
};
//注意:排序不会删除集合中的元素Collections.sort(list,comp); System.out.println("逆序: " + list);
//5.shuffle 随机打乱Collections.shuffle(list); System.out.println("打乱: " + list);
//6.sort 排序【默认自然排序】Collections.sort(list); System.out.println("自然排序: " + list);
//7.fill 填充Collections.fill(list, 20);
System.out.println("after fill: " + list);
//8.将ArrayList【线程不安全】的集合 转换成 线程安全集合
//List<Integer> list2 = Collections.synchronizedList(list);
}
}
注意:Collections.sort排序不会删除集合中的元素。