更好的阅读体验:点这里 ( www.doubibiji.com
)
更好的阅读体验:点这里 ( www.doubibiji.com
)
更好的阅读体验:点这里 ( www.doubibiji.com
)
9 集合
什么是集合?
集合就是可以存储多个数据的一种数据类型,集合内的每一个数据称之为元素,其中的元素可以是任意类型的数据,包括字符串、数字、布尔,甚至是集合等等。
在前面已经学习了数组,数组也是可以批量的存储数据,但是集合比数组要强大的多。
数字一旦初始化以后,长度就固定了,是无法改变的,而且类型也确定了,同时提供的方法有限,对添加、删除、插入、搜索等操作就很不方便。
在Java中常用的集合分为3类:
- List
- Set
- Map
不同的集合有不同的特点,例如:
- 是否支持重复的元素,有的集合中的元素不能重复,有的可以。
- 是否有序,有的集合中的元素是有序的,可以通过index来获取元素,有的集合是无序的,无法通过index获取元素。
下面一一介绍。
9.1 List
List 列表就是一个普通的集合,满足你最原始的想象,主要有如下特点:
- 列表中的元素可以重复;
- 列表中的元素可以修改,可以增加、修改、删除;
- 列表中的元素是有序的,可以通过索引来访问;
- 列表中可以存储不同数据类型的数据;
和数组很像,但是会自动扩容,支持不同类型的数据。
1 创建List
// 创建一个String类型的列表,<String>表示泛型
List<String> strList = new ArrayList<>();
// 创建一个Integer类型的列表
List<Integer> numbers = new ArrayList<>();
// 不指定类型,什么元素都可以放,和 List<Object> 一样
List objList = new ArrayList<>();
在创建集合的时候,可以通过 <类型>
泛型指定集合中元素的类型,关于泛型,后面在进阶篇再讲解。
指定泛型后,后面在获取集合中元素的时候,获取的数据就是泛型指定的类型;如果不指定泛型,那么元素在取出的时候是Object类型,那么赋值给指定类型的变量就需要强制转换,后面获取元素的时候再讲。
还可以在创建的时候,指定List初始化的大小,在使用的时候,如果元素超过了初始容量,会自动进行扩容。
如果知道列表中要放多少数据,建议在新建数组的时候指定列表大初始大小,这样避免扩容,从而耗费性能,因为列表的底层还是使用数组实现的,默认长度是10,而数组是无法动态修改大小的,所以在扩容的时候会创建一个新的列表,将之前列表中的数据拷贝到新列表中。
// 创建一个初始容量为5的ArrayList,用于存储Integer类型的元素
List<Integer> list = new ArrayList<>(5);
还可以创建不可变的List,不可变的List不能添加、修改、删除元素,只能读取元素,否则会报错:
List<String> colorList = List.of("red", "green", "blue");
System.out.println(colorList); // [red, green, blue]
如果想快速创建包含元素的可变 List,可以使用如下方式:
// 将List.of(1, 2, 3)作为参数创建一个新的可变List
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
2 基础操作
添加元素
通过 add()
和 addAll()
方法可以添加元素。
List<Integer> numList1 = new ArrayList<>();
numList1.add(1); // 添加元素
numList1.add(2);
List<Integer> numList2 = List.of(3, 4);
numList1.addAll(numList2); // 将numList2中所有的元素都添加到numList1中
访问元素
使用 get()
方法,通过下标来访问列表中的元素,从0开始,注意不要越界。
List<Integer> numbers = List.of(1, 2, 3);
Integer integer = numbers.get(0); // 访问元素:1
// 访问元素
System.out.println(integer); // 1
// 放入三个类型的数据,分别是integer、String、List列表
List itemList = List.of(1, "abc", List.of(1, 2, 3));
// 因为没有使用泛型,所以需要强制转换
Integer i = (Integer) itemList.get(0);
String s = (String) itemList.get(1);
List<Integer> list = (List<Integer>) itemList.get(2);
如果使用泛型,直接取出数据就是泛型的类型,如果没有使用泛型,那么取出的类型需要根据类型进行强制转换。
获取长度
通过 size()
方法,可以获取到List 的长度。
List<Integer> numbers = List.of(1, 2, 3);
// 获取长度
System.out.println(numbers.size()); // 3
修改元素
修改元素可以使用 set()
方法,修改指定位置的元素。
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
numbers.set(1, 5); // 修改元素
System.out.println(numbers); // [1, 5, 3]
插入元素
插入元素也是使用 add
和 addAll()
方法,使用第一个参数指定插入的位置即可。
List<Integer> numList1 = new ArrayList<>();
numList1.add(1); // 添加元素
numList1.add(1, 2); // 在index为1的位置添加元素
List<Integer> numList2 = List.of(3, 4);
// 将numList2插入到numList1中
numList1.addAll(2, numList2);
System.out.println(numList1); // [1, 2, 3, 4]
删除元素
删除元素可以通过 元素 和 index 来删除。
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
numbers.remove(Integer.valueOf(2)); // 通过元素移除,将值为2的元素移除
numbers.remove(1); // 通过index移除,移除index为1的元素
System.out.println(numbers); // [1]
清空List
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
numbers.clear(); // 清空List
System.out.println(numbers); // []
3 遍历List
当遍历 Java 中的列表时,您可以使用以下方法:
使用 for
循环
List<String> fruits = new ArrayList<>(List.of("apple", "banana", "orange"));
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
在 for
循环中,使用列表的长度 fruits.size()
作为循环条件,每个元素通过 get(index)
方法获取。
使用 for-each
循环
List<String> fruits = new ArrayList<>(List.of("apple", "banana", "orange"));
for (String fruit : fruits) {
System.out.println(fruit);
}
在 for-each
循环中,将每个列表元素 fruit
赋值给变量 String fruit
,然后在循环体中执行相应的操作。这种方法比使用 for
循环更简洁,特别是当不需要访问每个元素的索引时。
使用迭代器
List<String> fruits = new ArrayList<>(List.of("apple", "banana", "orange"));
// 获取迭代器
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
在使用迭代器遍历列表时,首先需要获取列表的迭代器 fruits.iterator()
,然后可以使用 while
循环和迭代器的 hasNext()
和 next()
方法来访问列表的每个元素。
遍历的时候删除元素
我们可能会有这样的场景,遍历一个 List,将其中满足条件的元素删除,例如删除一个 List 中的偶数,可能编写代码如下:
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5));
for (Integer num : numbers) {
if (num % 2 == 0) {
numbers.remove(num); // 删除元素
}
}
但是执行代码,却发现报错,如下:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
at com.demo.ListTest.main(ListTest.java:16)
当在迭代器遍历集合的同时,又对集合进行了结构性修改(比如添加、删除元素)时就会抛出 ConcurrentModificationException
异常。
那怎么在遍历的时候删除元素呢?
可以使用迭代器的 remove()
方法来安全地删除元素,举个栗子:
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3, 4, 5));
// 使用迭代器遍历列表
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer num = iterator.next();
// 在遍历时如果想要删除某个元素,使用迭代器的 remove 方法
if (num % 2 == 0) {
iterator.remove();
}
}
System.out.println(numbers); // 输出:[1, 3, 5]
4 排序
将列表中的元素按照从小到大排列:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Orange");
list.add("Banana");
Collections.sort(list);
System.out.println(list); // 输出:[Apple, Banana, Orange]
}
}
从大到小排列:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SortListExample {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Apple");
list.add("Orange");
list.add("Banana");
// 使用 reverseOrder() 方法实现降序排序
Collections.sort(list, Collections.reverseOrder());
System.out.println(list); // 输出应该是:[Orange, Banana, Apple]
}
}
但是使用自定义的对象放入到 List 中就无法排序了,因为自定义对象没有实现 Comparable
接口,所以自定义对象可以实现 Comparable
接口,重写 compareTo 方法,可以实现按照自定的属性进行排序:
import java.util.ArrayList;
import java.util.List;
class Student implements Comparable {
private String name;
private int age;
public Student(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 int compareTo(Object target) {
if (target instanceof Student) {
Student student = (Student) target;
return this.name.compareTo(student.name); //按照姓名从小到大排列
} else {
throw new RuntimeException("类型不匹配");
}
}
}
public class SortListExample {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan", 15));
list.add(new Student("lisi", 16));
list.add(new Student("wangwu", 17));
for (Student s : list) {
System.out.println(s.getName());
}
}
}
利用compare(Object target)
方法,比较当前对象和target的大小,如果想从小到大排列,则当前对象的属性小于 target 的属性,返回负数,如果相等,返回0,如果大于,返回正数,则就实现了从小到大排列,反之从大到小排列。
如果不想实现 Comparable
接口,也可以使用Collections.sort()
方法并传入一个Comparator
比较器对象。
举个栗子:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Student {
private String name;
private int age;
public Student(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;
}
}
public class SortListExample {
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
list.add(new Student("zhangsan", 15));
list.add(new Student("lisi", 17));
list.add(new Student("wangwu", 16));
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
if(o1 instanceof Student && o2 instanceof Student){
// 年龄相等就算相同, 按照年龄从小到大排列
return Integer.compare(o1.getAge(), o2.getAge());
}else{
throw new RuntimeException("类型不匹配");
}
}
});
for (Student s : list) {
System.out.println(s.getName());
}
}
}
5 其他常用操作
判断List是否为空
可以通过 isEmpty()
方法判断 List 是否为空。
List<Integer> numbers = new ArrayList<>();
if (numbers.isEmpty()) {
System.out.println("集合为空");
}
numbers.add(1);
numbers.add(2);
numbers.add(3);
if (!numbers.isEmpty()) {
System.out.println("集合不为空");
}
一般使用的时候,可以搭配 null 来判断一个 List 为非空,例如: if (null != numbers && !numbers.isEmpty())
。
检查包含某个元素
List<Integer> numbers = new ArrayList<>(List.of(1, 2, 3));
System.out.println(numbers.contains(2)); // true
9.2 Set
Set与List的区别:Set是一种不允许重复元素的集合类型,而且Set是无序的。
Set更重要的是用来做一些运算,例如求两个集合的交集、并集、差集等。
Set最主要的特点:
-
不支持重复的元素
-
内容是无序的,不能下标来访问元素
-
Set是可以被修改的
-
可以存储不同的数据类型
List 有序,是添加元素的时候,后添加的元素会排列到前面元素的后面。而无序,是每次添加的元素之间没有顺序,这里设计数据结构的相关知识,后面可以通过学习数据结构来了解。
1 创建Set
// 创建一个String类型的Set
Set<String> colorSet = new HashSet<>();
// 创建一个Integer类型的Set
Set<Integer> numSet = new HashSet<>();
// 不指定类型,什么元素都可以放,和 Set<Object> 一样
Set objList = new HashSet<>();
还可以在创建的时候,指定 Set 初始化的大小,在使用的时候,如果元素超过了初始容量,会自动进行扩容。
// 创建一个初始容量为5的HashSet,用于存储Integer类型的元素
Set<Integer> list = new HashSet<>(5);
还可以创建不可变的 Set,不可变的 Set 不能添加、修改、删除元素,只能读取元素:
Set<String> colorList = Set.of("red", "green", "blue");
System.out.println(colorList); // [red, green, blue]
如果想快速创建包含元素的可变 Set,可以使用如下方式:
// Set.of(1, 2, 3)作为参数创建一个新的可变Set
Set<Integer> numbers = new HashSet<>(Set.of(1, 2, 3));
System.out.println(numbers);
2 基础操作
添加元素
通过 add()
和 addAll()
方法可以添加元素。
Set<Integer> numSet1 = new HashSet<>();
// 添加元素
numSet1.add(1);
numSet1.add(2);
Set<Integer> numSet2 = Set.of(3, 4);
numSet1.addAll(numSet2); // 将numSet2中所有的元素都添加到numSet1中
访问元素
因为Set是无序的,所以无法通过下标来访问Set中的元素,如果想访问Set中的元素,只能通过遍历的方式来获取Set中的元素。
获取长度
通过 size()
方法,可以获取到 Set 的长度。
Set<Integer> numbers = Set.of(1, 2, 3);
// 获取长度
System.out.println(numbers.size()); // 3
删除元素
删除元素只能通过 元素 来删除,无法通过 index 来删除。
Set<Integer> numSet = new HashSet<>(Set.of(1, 2, 3));
// 删除元素
numSet.remove(2);
System.out.println(numSet); // [1, 3]
因为Set是无序的,所以无法通过下标来删除元素。
清空Set
Set<Integer> numSet = new HashSet<>(Set.of(1, 2, 3));
// 清空set
numSet.clear();
System.out.println(numSet); // []
将Set转换为List
Set<Integer> numSet = Set.of(1, 2, 3);
// 将set作为参数传递给List,其实这种方式也可以将List转换为Set
List<Integer> numList = new ArrayList<>(numSet);
System.out.println(numList);
3 遍历Set
当遍历 Set 时,您可以使用以下方法:
使用 for-each
循环
Set<String> colorSet = Set.of("red", "green", "blue");
for (String color : colorSet) {
System.out.println(color);
}
在这个示例中,我们使用 for-each
循环遍历了 Set 中的每个元素,并打印每个元素。
因为 Set 是无序的,所以无法使用简单的 for 循环通过 index 来遍历。
使用迭代器
Set 类型也实现了 Iterable
接口,因此您也可以使用迭代器来遍历 Set 中的元素。例如:
Set<String> colorSet = Set.of("red", "green", "blue");
// 获取迭代器
Iterator<String> iterator = colorSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
在这个示例中,我们首先获取了 Set 的迭代器,并使用 while
循环和 hasNext()
方法遍历了 Set 中的每个元素,并使用 next()
方法访问当前元素。
4 其他常用操作
因为 List 和 Set 都是继承自 Collection 接口,很多方法都是 Collection 接口中规范的,所以很多 List 有的操作 Set 也是有的。
判断Set是否为空
可以通过 isEmpty()
方法判断 Set 是否为空,通过 isNotEmpty()
方法判断 Set 是否不为空。
Set<Integer> numSet = new HashSet<>();
if (numSet.isEmpty()) {
System.out.println("Set为空");
}
numSet = new HashSet<>(Set.of(1, 2, 3, 4, 5));
if (!numSet.isEmpty()) {
System.out.println("Set不为空");
}
检查包含某个元素
Set<Integer> numSet = new HashSet<>(Set.of(1, 2, 3));
System.out.println(numSet.contains(2)); // true
并集
使用 addAll()
方法将两个 Set 合并为一个Set。下面是一个示例:
Set<Integer> set1 = new HashSet<>(Set.of(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Set.of(3, 4, 5));
set1.addAll(set2);
System.out.println(set1); // [1, 2, 3, 4, 5]
交集
使用 retainAll()
方法获取两个Set的交集。下面是一个示例:
Set<Integer> set1 = new HashSet<>(Set.of(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Set.of(3, 4, 5));
// 求交集
set1.retainAll(set2);
System.out.println(set1); // [3]
差集
使用 removeAll()
方法获取两个 Set 的差集。下面是一个示例:
Set<Integer> set1 = new HashSet<>(Set.of(1, 2, 3));
Set<Integer> set2 = new HashSet<>(Set.of(3, 4, 5));
// 求差集
set1.removeAll(set2);
System.out.println(set1); // [1, 2]
9.3 Map
Map是一种键值对的数据结构,其中每个键对应一个值。
例如有一份数据:
姓名 | 成绩 |
---|---|
zhangsan | 94 |
lisi | 96 |
wangwu | 91 |
使用List、Set的方式存储上面的数据是很不方便的。
而使用Map存储就很适合,可以将姓名作为key,成绩作为value。
{
"zhangsan": 94,
"lisi": 96,
"wangwu": 91
}
这样可以很容易通过姓名key得到对应的成绩value。
1 创建Map
// 创建一个String-String类型的Map
Map<String, String> map1 = new HashMap<>();
// 创建一个String-Integer类型的Map
Map<String, Integer> map2 = new HashMap<>();
Map<String, String> map
其中 <String, String>
分别表示 key
和 value
对应的数据类型。
同样,也是可以使用 Map.of()
来创建不可变的 Map,不可变的 Map 不能添加、修改、删除元素,只能读取元素:
Map<String, Integer> scoreMap = Map.of("zhangsan", 90, "lisi", 99,"wangwu", 80);
System.out.println(scoreMap); // {wangwu=80, lisi=99, zhangsan=90}
如果想快速创建包含元素的可变 Map,可以使用如下方式:
Map<String, Integer> scoreMap = new HashMap<>(Map.of("zhangsan", 90, "lisi", 99,"wangwu", 80));
System.out.println(scoreMap); // {wangwu=80, lisi=99, zhangsan=90}
2 基础操作
添加元素
添加元素使用 put()
方法:
Map<String, Integer> numbers = new HashMap<>();
// 添加元素
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);
System.out.println(numbers); // {one=1, two=2, three=3}
访问元素
访问元素的时候,通过 key 来访问。
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 获取元素
System.out.println(numbers.get("two")); // 2
如果访问不存在的key,则得到结果为null。
获取长度
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 获取长度
System.out.println(numbers.size()); // 3
删除元素
删除的时候,通过 key 来删除
Map<String, Integer> numbers = new HashMap<>(Map.of("one", 1, "two", 2, "three", 3));
// 删除元素
numbers.remove("two");
System.out.println(numbers); // {three=3, one=1}
清空Map
Map<String, Integer> numbers = new HashMap<>(Map.of("one", 1, "two", 2, "three", 3));
// 清空map
numbers.clear();
System.out.println(numbers); // {}
3 遍历操作
使用 for...in
循环
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
for (Map.Entry<String, Integer> entry : numbers.entrySet()) {
String key = entry.getKey();
Integer value = entry.getValue();
System.out.println("key:" + key + ", value:" + value);
}
这个示例使用了 for...in
循环遍历 Map,其中的 entrySet()
方法返回一个包含键值对的Set,然后在循环中使用 getKey()
和 getValue()
方法访问键和值。
使用 forEach()
方法
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 使用forEach方法遍历
numbers.forEach方法遍历((key, value) -> {
System.out.println("key:" + key + ", value:" + value);
});
这个示例使用了 forEach()
方法遍历 Map,其中的 lambda 表达式接收键和值,并在控制台打印出每个学生的分数。
使用 keys
或 values
属性
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 首先得到key组成的set,然后遍历set
for (String key : numbers.keySet()) {
Integer value = numbers.get(key);
System.out.println("key:" + key + ", value:" + value);
}
for (Integer value : numbers.values()) {
System.out.println("value:" + value);
}
这个示例使用了 keySet()
和 values()
方法分别遍历 Map 的键和值,然后在循环中使用键或值变量访问相应的键或值。
4 其他常用操作
判断Map是否为空
可以通过 isEmpty()
方法判断 Map 是否为空,通过 isNotEmpty()
方法判断 Map 是否不为空。
Map<String, Integer> numbers = new HashMap<>();
if (numbers.isEmpty()) {
System.out.println("Map为空");
}
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);
if (!numbers.isEmpty()) {
System.out.println("Map不为空");
}
检查包含某个键
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 是否包含某个key
System.out.println(numbers.containsKey("two")); // true
检查包含某个值
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 是否包含某个value
System.out.println(numbers.containsValue(2)); // true
获取Map中的所有键
Map<String, Integer> numbers = Map.of("one", 1, "two", 2, "three", 3);
// 获取所有的key
Set<String> keys = numbers.keySet();
System.out.println(keys); // [three, one, two] 无序的
9.4 集合的体系结构
其实看上面的 List 和 Set 方法是基本一样的,因为他们实现了共同的 Collection 接口。Map 是另外与 Collection 接口类似的顶级接口。
常用的集合的体系结构如下:
需要注意:ArrayList、HashSet、HashMap 都是非线程安全的,所以不能用在多个线程中共享数据,如果要使用线程安全的类,需要使用CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentHashMap。
下面再介绍一下可能会用到的其他的类,另外一些没介绍的,可以百度一下如何使用。
1 TreeSet
TreeSet
是 SortedSet
接口的实现类,添加到 TreeSet
中的元素会自动排序和去重。
所以 TreeSet 中只能放同类型的数据,不同类型的数据无法进行比较排序。
TreeSet
两种排序方法:自然排序和定制排序。默认情况下,TreeSet
采用自然排序。
举个栗子:
自然排序法
自然排序要求存储在TreeSet
中的元素实现Comparable
接口,并覆写compareTo(Object o)
方法。
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args) {
Set<String> treeSet = new TreeSet();
treeSet.add("edg");
treeSet.add("abc");
treeSet.add("bcd");
treeSet.add("abc");
for (String s : treeSet) {
System.out.println(s);
}
}
}
输出结果:
abc
bcd
edg
从上面的代码可以看出,对加入的字符串进行排序,默认使用的是自然排序。因为String 类实现了Comparable
接口
自然排序排列对象
如果将自定义的对象添加的 TreeSet 中的时候,发现会报错,因为该对象没有实现 Comparable 接口。
所以如果使用自然排序,放入到 TreeSet 中的类对象,需要实现 Comparable 接口。
import java.util.Set;
import java.util.TreeSet;
class Student implements Comparable {
private String name;
private int age;
public Student(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 int compareTo(Object target) {
if (target instanceof Student) {
Student student = (Student) target;
return this.name.compareTo(student.name); //按照姓名从小到大排列
} else {
throw new RuntimeException("类型不匹配");
}
}
}
public class TreeSetTest {
public static void main(String[] args) {
Set<Student> treeSet = new TreeSet();
treeSet.add(new Student("zhangsan", 15));
treeSet.add(new Student("zhangsan", 15));
treeSet.add(new Student("lisi", 16));
treeSet.add(new Student("wangwu", 17));
for (Student s : treeSet) {
System.out.println(s.getName());
}
}
}
上面的 Student 类实现了 Comparable 接口,并实现了 compareTo() 方法。利用compare(Object target)
方法,比较当前对象和target的大小,如果想从小到大排列,则当前对象的属性小于 target 的属性,返回负数,如果相等,返回0,如果大于,返回正数,则就实现了从小到大排列,反之从大到小排列。上面是直接通过name调用String 类的 compareTo 方法实现返回值。
需要注意,添加到 TreeSet 中,比较两个对象是否相等,是通过 compareTo 方法判断的,不是equals。
定制排序
当默认的自然排序不满足需求时,可以使用定制排序,定制排序通常通过传递一个实现了Comparator
接口的比较器对象给TreeSet
的构造函数来实现,通过实现compare(Object o1, Object o2)
方法来实现排序逻辑,使用定制排序,可以灵活地定义元素之间的顺序,而不必要求元素自身实现Comparable
接口。
举个栗子:
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
// 不用实现接口了
class Student {
private String name;
private int age;
public Student(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;
}
}
public class TreeSetTest {
public static void main(String[] args) {
Comparator<Student> comparator = new Comparator<Student>() {
//按照年龄从小到大排列
@Override
public int compare(Student o1, Student o2) {
if(o1 instanceof Student && o2 instanceof Student){
// 年龄相等就算相同
return Integer.compare(o1.getAge(), o2.getAge());
}else{
throw new RuntimeException("类型不匹配");
}
}
};
Set<Student> treeSet = new TreeSet(comparator);
treeSet.add(new Student("zhangsan", 15));
treeSet.add(new Student("zhangsan", 15));
treeSet.add(new Student("lisi", 15));
treeSet.add(new Student("wangwu", 17));
for (Student s : treeSet) {
System.out.println(s.getName());
}
}
}
执行结果:
zhangsan
wangwu
2 Properties
Properties
类是Hashtable
的子类,主要用于处理属性文件,Properties
里的key
和value
都是字符串类型。
存储数据使用setProperty(String key,Stringvalue)
方法,获取数据使用getProperty(String key)
方法。
举个例子:
首先在项目根目录下新建一个 .properties
文件:
编辑内容如下:
username=doubibiji
password=123456
上面就是使用了 key=value 的方式定义两个配置。
下面使用 Properties 读取配置:
package com.doubibiji;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args){
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("test.properties");
//加载文件中的配置到Properties对象中
pros.load(fis);
// 获取配置
String username = pros.getProperty("username");
String password = pros.getProperty("password");
System.out.println("username=" + username); // username=doubibiji
System.out.println("password=" + password); // password=123456
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
如果存在中文乱码问题,可以使用如下方式解决: