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.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是一种键值对的数据结构,其中每个键对应一个值。

例如有一份数据:

姓名成绩
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;
    }

    @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();
                }
            }
        }
    }
}

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

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

  • 28
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山石岐渡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值