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;机器人控制也在不断进步和演变。智能化机器人具备…

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…

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

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

java技术:oauth2协议

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

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 和键值的读写是单…

数据库|基于T-SQL创建数据库

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; SQL Server用于操作数据库的编程语言为Transaction-SQL,简称T-SQL。 本节学习基于T-SQL创建数据库。以下为学习笔记。 01 打开新建查询 首先连接上数据库&#xff0c;点击【新建查询】打开新建查询窗口&#xff0c; …

C++—结构体

结构体&#xff08;struct&#xff09;&#xff0c;是一种用户自定义复合数据类型&#xff0c;可以包含不同类型的不同成员。 结构体的声明定义和使用的基本语法&#xff1a; // 声明结构体struct 结构体类型 { 成员1类型 成员1名称; ...成员N类型 成员N名称; };除声明…

【计算机视觉(2)】

基于Python的OpenCV基础入门——视频的处理 视频OpenCV视频处理操作&#xff1a;创建视频对象判断视频是否成功初始化读取视频帧获取视频特征设置视频参数声明编码器保存视频释放视频对象 视频处理基本操作的代码实现&#xff1a; 视频 视频是由一系列连续的图像帧组成的。每一…

Spring—IoC

目录 1. IoC的提出 2. Spring容器 2.1. Spring容器实现原理 2.2. Spring组件 2.2.1 XML标签方式 2.2.2. 类注解方式 2.2.3. 方法注解方式 2.3. Spring容器分类 2.3.1. BeanFactory容器 2.3.2. ApplicationContext容器 2.3.3. WebApplicationContext容器 3. Spring中…

Srping 历史

一、History of Spring and the Spring Framework Spring came into being in 2003 as a response to the complexity of the early J2EE specifications. While some consider Java EE and its modern-day successor Jakarta EE to be in competition with Spring, they are …

idea启动报错:java.lang.NoClassDefFoundError: org/mybatis/logging/LoggerFactory

文章目录 一、问题二、解决方法 一、问题 问题描述&#xff1a;idea整合Mybatis-plus的时候&#xff0c;启动报错&#xff1a;java.lang.NoClassDefFoundError: org/mybatis/logging/LoggerFactory 二、解决方法 可能原因&#xff1a;仔细检查了一下&#xff0c;发现 mybati…

《王者荣耀》4月狂揽2.34亿美元 单日流水1亿美元 全球销量第二

易采游戏网5月24日消息&#xff0c;在刚刚过去的四月&#xff0c;全球手游市场迎来了一场收益的盛宴&#xff0c;其中《王者荣耀》以其惊人的吸金能力&#xff0c;以2.34亿美元的月收入在全球手游排行榜上位列第二。4月5日&#xff0c;这款由腾讯游戏开发的多人在线战斗竞技游戏…

C++相关概念和易错语法(14)(初始化注意事项、vector、编译器向上查找规则)

1.当我们在代码中想要终止运行的话&#xff0c;我们可以采用Ctrl C或Ctrl Z&#xff0c;其中^C代表杀进程&#xff0c;^Z设置结束2.编码表&#xff1a;我们目前比较熟悉的是ASCII码编码方式&#xff0c;但是我们发现平时使用的汉字无法通过ASCII编码&#xff0c;除此之外&…