Java基础教程 - 9 集合

更好的阅读体验:点这里 ( 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]
插入元素

插入元素也是使用 addaddAll() 方法,使用第一个参数指定插入的位置即可。

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.ConcurrentModificationExceptionat 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;}@Overridepublic 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>() {@Overridepublic 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是一种键值对的数据结构,其中每个键对应一个值。

例如有一份数据:

姓名成绩
zhangsan94
lisi96
wangwu91

使用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> 分别表示 keyvalue 对应的数据类型。

同样,也是可以使用 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 表达式接收键和值,并在控制台打印出每个学生的分数。

使用 keysvalues 属性

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

TreeSetSortedSet 接口的实现类,添加到 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;}@Overridepublic 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>() {//按照年龄从小到大排列@Overridepublic 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=doubibijiSystem.out.println("password=" + password); // password=123456} catch (IOException e) {e.printStackTrace();} finally {if(fis != null){try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}
}

如果存在中文乱码问题,可以使用如下方式解决:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/14002.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【stm32/CubeMX、HAL库】嵌入式实验六:定时器(2)|PWM输出

参考&#xff1a; 【【正点原子】手把手教你学STM32CubeIDE开发】 https://www.bilibili.com/video/BV1Wp42127Cx/?p13&share_sourcecopy_web&vd_source9332b8fc5ea8d349a54c3989f6189fd3 《嵌入式系统基础与实践》刘黎明等编著&#xff0c;第九章定时器&#xff0c…

爱普生TG5032SFN温补晶振在机器人控制中的应用

机器人控制是机器人技术的核心组成部分&#xff0c;它涉及通过传感器采集外部环境信息&#xff0c;然后经过信号处理、运动规划和执行控制等步骤&#xff0c;最终实现机器人的运动控制和任务执行。在技术的不断更选&#xff0c;机器人控制也在不断进步和演变。智能化机器人具备…

cannot compute sizeof(off_t) when compile netcdf-fortran

export LD_LIBRARY_PATH/netcdf-c/lib:$LD_LIBRARY_PATH

Z缓冲技术在AI去衣中的关键角色

引言&#xff1a; 人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;为图像处理领域带来了革命性的变化。其中&#xff0c;AI去衣技术作为一种新兴的应用&#xff0c;引起了广泛关注。它不仅在多媒体内容的编辑、虚拟现实和增强现实等领域具有重要的应用价值&…

Jenkins 构建 Maven 项目:项目和服务器在一起的情况

bash.sh内容 #!/bin/bash#删除历史数据 rm -rf ruoyi-admin.jar# appname$1 appnamevideo.xxxxx.com #获取传入的参数 echo "arg:$appname"#获取正在运行的jar包pid # pidps -ef | grep $1 | grep java -jar | awk {printf $2} pidps -ef | grep $appname | grep ja…

1673. 找出最具竞争力的子序列

题目 给定一个整数数组 nums 和一个正整数 k&#xff0c;返回长度为 k 且最具竞争力的 nums 子序列。 数组的子序列是从数组中删除一些元素&#xff08;可能不删除元素&#xff09;得到的序列。 在子序列 a 和子序列 b 第一个不相同的位置上&#xff0c;如果 a 中的数字小于…

mysql 删除特殊字符 表中存了特殊字符 换行符 回车符 word字符 查询不到

省流&#xff1a; UPDATE t1 SET f1 REPLACE(REPLACE( f1 , CHAR(10), ), CHAR(13), ); 用 replace() 函数将 换行符char(10) 和 回车符char(13) 替换为空字符串。 char(10)&#xff1a;换行 char(13)&#xff1a;回车 发现表里存进很多换行符&#xff0c;如下图&#xff1a…

深入研究Qt Meta - Object System

目录 先说RTTI 再说QMeta Object System 关于Q_OBJECT 这篇文章我打算研究一下QMetaObject System&#xff0c;也就是Qt自己构建起来的元对象系统。 先说RTTI 啥是RTTI&#xff1f;这是C编程里的一个常见术语&#xff0c;全称是&#xff1a;运行阶段类型识别&#xff08;Ru…

Chrome DevTools攻略

Chrome DevTools&#xff0c;也称为Chrome开发者工具&#xff0c;是一套直接内置于Google Chrome浏览器的Web开发者工具。以下是一些使用Chrome DevTools的攻略和技巧&#xff1a; 打开DevTools&#xff1a; 右键点击页面上的任何元素&#xff0c;选择“检查”或“审查元素”。…

2024年华为OD机试真题-机场航班调度程序-C++-OD统一考试(C卷D卷)

题目描述: XX市机场停放了多架飞机,每架飞机都有自己的航班号CA3385,CZ6678,SC6508等,航班号的前2个大写字母(或数字)代表航空公司的缩写,后面4个数字代表航班信息。但是XX市机场只有一条起飞用跑道,调度人员需要安排目前停留在机场的航班有序起飞。为保障航班的有序起…

【webrtc】MediaEngine的实现CompositeMediaEngine创建VOE

m98音视频的引擎是管理channel的看起来是外部强加给CompositeMediaEngine 管理的。CompositeMediaEngine :合成媒体引擎 G:\CDN\rtcCli\m98\src\media\base\media_engine.h// CompositeMediaEngine constructs a MediaEngine from separate // voice and video engine classes…

Python中文分词工具库之jieba使用详解

概要 在自然语言处理(NLP)领域,中文文本的分词是一个重要且基础的任务。Python的jieba库是一个广泛使用的中文分词工具,提供了丰富的功能,包括精准模式、全模式、搜索引擎模式等,适用于不同的应用场景。本文将详细介绍jieba库,包括其安装方法、主要特性、基本和高级功能…

代码随想录35期Day49-Java

Day49题目 LeetCode123买卖股票三 核心思想:和昨天的买卖股票相比,这个只允许买两次,因此把状态新增几个,可见代码注释 class Solution {public int maxProfit(int[] prices) {// 设置五个状态 0 : 无操作 , 1 : 第一次买入, 2 : 第一次卖出 , 3: 第二次买入, 4:第二次卖出…

java技术:oauth2协议

目录 一、黑马程序员Java进阶教程快速入门Spring Security OAuth2.0认证授权详解 1、oauth服务 WebSecurityConfig TokenConfig AuthorizationServer 改写密码校验逻辑实现类 2、oauth2支持的四种方式&#xff1a; 3、oauth2授权 ResouceServerConfig TokenConfig 4、…

前端面试题日常练-day19 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末。 1. AJAX是什么的缩写&#xff1f; A. Asynchronous JavaScript and XMLB. Asynchronous JavaScript and XHTMLC. Asynchronous Java and XMLD. Asynchronous Java and XHTML2. 下列哪个方法用于创建…

SpringCloudAlibaba 动态读取配置文件的信息

传统读取方式&#xff1a; 在application.properties中写入要读取的内容&#xff0c;如下&#xff1a; coupon.user.nameTom coupon.user.age27 接口引入处&#xff1a; Value("${coupon.user.name}")private String name;Value("${coupon.user.age}")p…

MySQL的索引是什么

MySQL的索引 一、索引概述二、索引结构1.简要概述2.从二叉树说起3.再在说下B-Tree4.为什么选择BTree5.Hash又是什么6.博主被面试官经常问的题目 三、索引分类四、聚集索引&二级索引五、索引语法 一、索引概述 1.索引是帮助MySQL 高效获取数据的数据结构(有序)。在数据之外…

[STM32-HAL库]Flash库-HAL库-复杂数据读写-STM32CUBEMX开发-HAL库开发系列-主控STM32F103C6T6

目录 一、前言 二、实现步骤 1.STM32CUBEMX配置 2.导入Flash库 3.分析地址范围 4.找到可用的地址 5.写入读取普通数据 6.写入读取字符串 6.1 存储相关信息 6.2 存取多个参数 三、总结及源码 一、前言 在面对需要持久化存储的数据时&#xff0c;除了挂载TF卡&#xff0c;我们…

燃数科技前端25-40K*14薪一面超简单,下周二面啦

一面 1、自我介绍 2、低代码如何设计的 3、react路由原理 4、react生命周期 5、什么是回调地狱&#xff0c;如何解决 6、jwt和session有什么区别 7、js文件相互引用有什么问题&#xff1f;如何解决 8、一个很大的json文件&#xff0c;前端读取如何优化 面试我的不像是…

为什么说 Redis 是单线程的?——Java全栈知识(25)

为什么说 Redis 是单线程的&#xff1f; 我们常说的 Redis 是单线程的&#xff0c;但是我前面在讲持久化机制的时候又说 RDB 的持久化是通过主进程 fork 出一个子进程来实现 RDB 持久化。那么 Redis 到底是多线程还是单线程的呢&#xff1f; Redis 的网络 IO 和键值的读写是单…