目录
6.2.5 containsValue(Object value)
1、概述
Java中的集合(Collection)是一种用于存储和操作一组对象的数据结构。 集合框架提供了一组接口和类,用于处理集合中的元素。 它提供了各种类型的集合,如列表(List)、集(Set)、映射(Map)等。
集合框架的核心接口是Collection接口,它定义了一组通用的方法,可以用于操作集合中的元素。 Collection接口的常见实现类有List、Set和Queue。
List是一个有序的集合,允许重复元素。 常见的List实现类有ArrayList和LinkedList。 ArrayList是基于数组实现的,支持随机访问; LinkedList是基于链表实现的,支持高效的插入和删除操作。
Set是一个不允许重复元素的集合,它没有定义顺序。 常见的Set实现类有HashSet和TreeSet。 HashSet基于哈希表实现,具有快速的插入和查找操作; TreeSet基于红黑树实现,可以对元素进行排序。
Map是一种键值对的集合,每个键对应一个值。 常见的Map实现类有HashMap和TreeMap。 HashMap基于哈希表实现,具有快速的插入和查找操作; TreeMap基于红黑树实现,可以对键进行排序。
除了上述常见的集合类型,Java还提供了其他一些集合接口和类,如Queue、Deque、SortedSet、SortedMap等,可以根据实际需求选择适合的集合类型。
集合框架提供了丰富的方法和算法,可以方便地对集合进行操作和处理。 它提供了迭代器(Iterator)用于遍历集合中的元素,还提供了各种方法用于添加、删除、查找、排序等操作。
总之,Java中的集合框架提供了一组强大而灵活的数据结构,可以方便地处理和操作一组对象。 通过选择合适的集合类型和使用集合框架提供的方法,可以提高代码的效率和可读性。
2.Collection
Collection 和 Map 是 Java 中两个不同的接口,它们之间有一些相似之处,但也有一些重要的区别。
Collection 是一个接口,代表一组对象的集合,它是一种单值的集合,每个元素都是独立的。 Collection 接口有多个实现类,如 List、Set 和 Queue。
Map 也是一个接口,代表一组键值对的集合,它是一种键值对的集合,每个元素都包含一个键和一个值。 Map 接口有多个实现类,如 HashMap、TreeMap 和 LinkedHashMap。
可以说,Map 是 Collection 的一种特殊形式,它的每个元素都是一个键值对,而不是单个的对象。 在 Map 中,每个键都是唯一的,而值可以重复。
另外,Map 接口还提供了一些特殊的方法来操作键值对,如根据键获取值、根据键删除键值对等。 而 Collection 接口主要提供了一些通用的方法来操作集合中的元素,如添加元素、删除元素、判断元素是否存在等。
总结来说,Collection 是一种单值的集合,而 Map 是一种键值对的集合。 它们之间有一些相似之处,但也有一些重要的区别。
2.1 Collection集合创建
因为Collection、List、Set、Map 都是接口,不能直接创建对象并且使用 而ArrayList、LinkedList、HashSet、TreeSet、HashMap 是其子类,可实例化对象,所以创建Collection集合可以采用多态的方式。
同时,因为多态允许我们使用父类类型的引用来引用子类类型的对象。 在这种情况下,Collection接口作为父类类型,具体的集合类(如ArrayList、HashSet等)作为子类类型。 通过使用多态,我们可以在不改变代码的情况下,轻松地切换不同的具体集合类。
import java.util.ArrayList;
import java.util.Collection;
/*
创建Collection对象
用多态的方式
ArrayList()
*/
public class CollectionDemo {
public static void main(String[] args) {
//创建Collection集合的对象
Collection<String> c = new ArrayList<String>();
//添加元素 boolean add (E e)
c.add("hello");
c.add("world");
//输出集合对象
//输出的 不是地址 而是存储到集合里面的数据 说明 ArrayList 重写了 toString() 方法
System.out.println(c);
}
}
运行结果
[hello, world]
2.2 Collection集合的常用方法
2.2.1.add()
add(E e)
方法用于向集合中添加指定元素 。如果集合中已经存在该元素,则不会再次添加,并且该方法会返回 。如果添加成功,则返回 。e
false
true
例如,下面的代码创建了一个 集合并向其中添加元素:ArrayList
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// 创建一个 ArrayList 集合
ArrayList<String> list = new ArrayList<>();
// 向集合中添加元素
list.add("apple");
list.add("banana");
list.add("orange");
// 输出集合的大小和内容
System.out.println("集合的大小为:" + list.size());
System.out.println("集合的内容为:" + list);
}
}
输出结果为:
集合的大小为:3
集合的内容为:[apple, banana, orange]
2.2.2.remove()
remove(Object o)
方法用于将指定元素从集合中移除,并返回一个 boolean 值表示是否移除成功。如果集合中不存在指定元素,则返回 false;否则将该元素从集合中移除并返回 true。
例如,假设有一个 ArrayList 存储了一些整数:
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
可以使用 方法从集合中移除指定元素:remove
list.remove(2); // 将 2 从集合中移除
此时,集合中的元素为 1、3 和 4。如果要移除的元素不存在于集合中,则返回 false:
boolean result = list.remove(5);
System.out.println(result); // 输出 false
因为 5 不在集合中,所以 方法返回 false。remove
2.2.3.size()
集合中的 size() 方法是用于获取集合中元素的数量的方法。它返回一个 int 类型的值,表示集合中元素的数量。
使用方式:
集合.size();
示例:
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("JavaScript");
System.out.println(list.size()); // 输出:3
Set<Integer> set = new HashSet<>();
set.add(1);
set.add(2);
set.add(3);
System.out.println(set.size()); // 输出:3
在上面的示例中,我们分别创建了一个 List 和一个 Set 集合,然后向集合中添加了元素。最后,通过 size() 方法获取了集合中的元素数量,并将其输出到控制台。
需要注意的是,该方法只适用于集合类,不能用于数组。如果要获取数组的长度,应该使用数组的 length 或者 Arrays 类的 length() 方法。
2.2.4.clear ()
在Java语言中,clear()是集合类中的一个方法,用于清空集合中的所有元素。该方法没有返回值。
示例代码:
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
System.out.println("清空前的集合:" + list);
list.clear();
System.out.println("清空后的集合:" + list);
输出结果:
清空前的集合:[apple, banana, orange]
清空后的集合:[]
在上面的示例中,我们创建了一个ArrayList集合,并向该集合中添加了三个元素。然后我们调用了clear()方法清空集合中的所有元素,并在控制台上输出了清空前后的集合内容。可以看到,清空后集合中的元素已经被删除了,集合变成了空集合。
2.2.5.contains()
contains(Object o)
方法用于判断集合中是否存在指定元素,并返回一个布尔值。如果集合包含指定元素,则返回 true,否则返回 false。
例如,假设有一个 ArrayList 容器,其中包含以下元素:
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
如果想判断该容器中是否包含 "banana" 这个元素,可以使用 contains()
方法来实现,例如:
boolean isContain = list.contains("banana");
System.out.println(isContain); // 输出 true
如果想判断该容器中是否包含 "pear" 这个元素,可以使用 contains()
方法来实现,例如:
boolean isContain = list.contains("pear");
System.out.println(isContain); // 输出 false
2.2.6.isEmpty()
判断集合是否为空
下面是使用 Collection 接口中的 isEmpty() 方法来判断集合是否为空的示例:
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo {
public static void main(String[] args) {
// 创建一个空的 ArrayList
Collection<String> list = new ArrayList<>();
// 判断集合是否为空
if (list.isEmpty()) {
System.out.println("集合为空");
} else {
System.out.println("集合不为空");
}
}
}
输出结果:
集合为空
在上面的例子中,我们创建了一个空的 ArrayList 集合,然后使用了 isEmpty() 方法来判断集合是否为空。由于这个集合是空的,所以输出结果为“集合为空”。
2.3 Collection集合遍历
遍历Collection集合的步骤如下:
-
创建一个迭代器对象,通过调用Collection的
iterator()
方法得到,例如:Iterator<E> iterator = collection.iterator();
-
使用
hasNext()
方法判断是否还有下一个元素,如果有则返回true
,否则返回false
,例如:while (iterator.hasNext()) { // 迭代中的代码 }
-
使用
next()
方法获取下一个元素,例如:while (iterator.hasNext()) { E element = iterator.next(); // 对元素进行处理 }
完整的示例代码如下:
Collection<String> collection = new ArrayList<>();
collection.add("a");
collection.add("b");
collection.add("c");
//创建迭代器
Iterator<String> iterator = collection.iterator();
//判断是否存在下一个元素
while (iterator.hasNext()) {
//获取集合中的元素
String element = iterator.next();
//输出集合元素
System.out.println(element);
}
集合遍历实例
下面是一个使用Collection集合遍历足球运动员实例的示例代码:
FootballPlayer.java(足球运动员实体类)
public class FootballPlayer {
private String name;
private int age;
private String club;
public FootballPlayer(String name, int age, String club) {
this.name = name;
this.age = age;
this.club = club;
}
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 String getClub() {
return club;
}
public void setClub(String club) {
this.club = club;
}
@Override
public String toString() {
return "FootballPlayer{" +
"name='" + name + '\'' +
", age=" + age +
", club='" + club + '\'' +
'}';
}
}
FootballPlayerTest.java(足球运动员测试类)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class FootballPlayerTest {
public static void main(String[] args) {
Collection<FootballPlayer> players = new ArrayList<>();
players.add(new FootballPlayer("Cristiano Ronaldo", 36, "Manchester United"));
players.add(new FootballPlayer("Lionel Messi", 34, "Paris Saint-Germain"));
players.add(new FootballPlayer("Neymar", 29, "Paris Saint-Germain"));
// 使用迭代器遍历
Iterator<FootballPlayer> iterator = players.iterator();
while (iterator.hasNext()) {
FootballPlayer player = iterator.next();
System.out.println(player);
}
}
}
输出结果:
FootballPlayer{name='Cristiano Ronaldo', age=36, club='Manchester United'}
FootballPlayer{name='Lionel Messi', age=34, club='Paris Saint-Germain'}
FootballPlayer{name='Neymar', age=29, club='Paris Saint-Germain'}
以上代码中,我们创建了一个Collection
类型的集合,使用add()
方法添加了三个足球运动员对象,然后使用迭代器遍历了这个集合,并打印出了每个元素的值。
3.List集合
3.1 List集合概述
在Java中,List是一种顺序集合,它可以存储一组元素,并允许访问和操作这些元素。List接口继承自Collection接口,它提供了一些特殊的方法来访问列表中的元素,例如get、set、add、remove等方法。常用的List实现类有ArrayList、LinkedList、Vector等。
-
ArrayList:基于动态数组实现,插入和删除元素效率较低,查找效率较高。
-
LinkedList:基于双向链表实现,插入和删除元素效率较高,查找效率较低。
-
Vector:基于动态数组实现,线程安全,但是插入和删除元素效率较低,查找效率较高。
List集合有以下特点:
-
List中的元素是有序的,可以按照添加的顺序来访问元素。
-
List中的元素可以重复。
-
List中可以存储null值。
-
List中有索引,可以通过索引值来访问元素。
使用List集合可以方便地对多个元素进行操作,例如遍历元素、添加元素、删除元素、获取元素等。
3.2 List集合的常用方法
3.2.1.add()
add()方法是向List集合中添加元素的基本方法。下面是一个例子:
List<String> list = new ArrayList<String>(); // 创建一个ArrayList对象
// 添加元素
list.add("apple");
list.add("banana");
list.add("orange");
// 在指定位置添加元素
list.add(1, "pear");
// 输出集合元素
System.out.println(list); // [apple, pear, banana, orange]
上述代码创建了一个ArrayList对象list,并向其添加了三个元素 "apple"、"banana"和"orange"。然后使用add()方法在位置1处插入了一个元素"pear"。最后输出集合元素,结果为[apple, pear, banana, orange]。
3.2.2.remove()
使用remove()方法可以从List集合中移除指定元素。下面是一个例子:
List<String> list = new ArrayList<String>();
// 添加元素
list.add("apple");
list.add("banana");
list.add("orange");
list.add("pear");
list.add("banana");
// 移除指定元素
list.remove("banana");
// 输出集合元素
System.out.println(list); // [apple, orange, pear, banana]
上述代码创建了一个ArrayList对象list,并向其添加了5个元素。然后使用remove()方法移除了第2个出现的"banana"元素。最后输出集合元素,结果为[apple, orange, pear, banana]。
3.2.3.set()
使用set()方法可以修改List集合指定位置的元素。下面是一个例子:
List<String> list = new ArrayList<String>();
// 添加元素
list.add("apple");
list.add("banana");
list.add("orange");
// 修改指定位置的元素,返回被修改的元素
list.set(1, "pear");
// 输出集合元素
System.out.println(list); // [apple, pear, orange]
上述代码创建了一个ArrayList对象list,并向其添加了3个元素。然后使用set()方法将第2个元素修改为"pear"。最后输出集合元素,结果为[apple, pear, orange]。
3.2.4.get()
使用get()方法可以获取List集合指定位置的元素。下面是一个例子:
List<String> list = new ArrayList<String>();
// 添加元素
list.add("apple");
list.add("banana");
list.add("orange");
// 获取指定位置的元素
String fruit = list.get(1);
// 输出元素
System.out.println(fruit); // banana
上述代码创建了一个ArrayList对象list,并向其添加了3个元素。然后使用get()方法获取第2个元素,并将其赋值给变量fruit。最后输出变量fruit的值,结果为"banana"。
3.3 List集合遍历
下面是一个使用List集合遍历足球学生实例的完整示例代码,包括实体类和测试类:
Student.java(实体类):
public class Student {
String name;
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
List.java(测试类):
import java.util.ArrayList;
import java.util.Iterator;
public class List {
public static void main(String[] args) {
//创建List集合
java.util.List<Student> l = new ArrayList<Student>();
//创建学生类对象
Student student1 = new Student("梅西",34);
Student student2 = new Student("内马尔",31);
Student student3 = new Student("C罗",35);
//添加学生信息到List集合中
l.add(student1);
l.add(student2);
l.add(student3);
//创建迭代器
Iterator<Student> it = l.iterator();
System.out.println("迭代器遍历学生数据:");
while (it.hasNext()){
Student next = it.next();
System.out.println(next.getName()+","+next.getAge());
}
System.out.println("for循环遍历学生数据:");
for (int i = 0;i<l.size();i++){
Student student = l.get(i);
System.out.println(student.getName()+","+student.getAge());
}
}
}
上述代码创建了一个足球学生的实体类Student,并在测试类List中创建了一个Student类型的List集合,向其中添加了5个足球学生实例。然后使用迭代器和for循环遍历集合中的所有元素,并打印出每个足球运动员实例的信息。
输出结果如下:
迭代器遍历学生数据:
梅西,34
内马尔,31
C罗,35
for循环遍历学生数据:
梅西,34
内马尔,31
C罗,35
3.4 并发修改异常
3.4.1概述
并发修改异常指在多线程环境下,当多个线程同时对同一数据进行修改时,会出现数据不一致的情况,也就是同时修改同一个数据。这个异常通常发生在并发编程中,例如在多个线程同时对同一个共享变量进行读写操作时,其中一个线程修改了该共享变量的值,而其他线程可能仍然持有该共享变量的旧值,导致数据不一致或者操作失败的情况。为了避免并发修改异常的发生,需要采用同步机制来保证在任何时刻最多只有一个线程可以修改该数据,或者使用线程安全的数据结构。
3.4.2List中涉及的并发修改异常
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
//创建 List集合
List<String> l = new ArrayList<String>();
//将数据添加进集合中
l.add("java");
l.add("world");
l.add("hello");
//创建迭代器
Iterator<String> iterator = l.iterator();
//迭代器遍历集合数据 会报错 因为next中的方法会将实际修改次数和预计修改次数进行比较 如果不相等 会报错 抛出 并发修改异常 而 add 方法会将实际修改次数+1
// while(iterator.hasNext()){
// String next = iterator.next();
// if(next.equals("java")){
// l.add("python");
// }
// System.out.println(next);
//
// }
//用 for循环 不会报错 因为 没有用到 next方法 而是用到 get方法 get方法中没有 将实际修改次数+1 的操作 所以 不会报错
for (int i=0;i<l.size();i++){
String s = l.get(i);
if (s.equals("java")){
l.add("python");
}
System.out.println(s);
}
}
}
3.5 ListIterator
3.5.1 概述
列表迭代器是一种在List集合中进行迭代的工具,它允许程序员沿任一方向遍历列表。相比于普通的迭代器,列表迭代器具有以下特点:
- 可以在迭代过程中修改列表元素,包括增加、删除、修改操作。
- 支持双向遍历,即可以向前或向后遍历列表。
- 可以获取当前迭代器的位置,并支持在指定位置插入、删除元素。
- 每个列表都有一个关联的列表迭代器,可以通过listIterator()方法获取。
需要注意的是,当使用列表迭代器修改列表元素时,必须使用列表迭代器的方法进行操作,不能使用List集合的方法,否则会出现ConcurrentModificationException异常。此外,列表迭代器的性能相较于普通迭代器会略有降低,因为它需要维护更多的状态信息。
3.5.2 ListIterator操作实例
假设有一个List集合存储了几个字符串,我们可以使用ListIterator对其进行迭代和修改操作。例如:
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("cherry");
ListIterator<String> iterator = list.listIterator();
//向后遍历
while(iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}
//向前遍历
while(iterator.hasPrevious()) {
String str = iterator.previous();
System.out.println(str);
}
//在指定位置插入元素
iterator.add("orange");
//删除元素
iterator.remove();
//修改元素
iterator.set("strawberry");
上述代码中,我们首先通过list.listIterator()方法获取ListIterator对象,然后使用next()和previous()方法进行遍历。在遍历的过程中,我们使用add()方法在指定位置插入元素,使用remove()方法删除元素,使用set()方法修改元素。
需要注意的是,修改和删除元素操作必须在调用next()或previous()方法之后进行,否则会出现IllegalStateException异常。
拓展:
//ListIterator 的 add() 方法里面 会将 实际修改值赋值给预期修改值,所以不会抛出异常
while(iterator.hasNext()){
String next = iterator.next();
if (next.equals("apple")){
iterator.add("javaee");
}
}
System.out.println(list);
[apple, javaee, banana, cherry]
3.6 增强for循环
3.6.1 概述
增强for循环,也称为foreach循环,是Java 5中新增的一种循环语法结构。它是基于迭代器设计的,可以更加方便地遍历数组和集合中的元素,相比传统的for循环和迭代器,增强for循环使用起来更加简单、易懂、且不易出错。
增强for循环的语法格式如下:
for (数据类型 变量名 : 数组/集合) {
//循环体
}
其中,数据类型表示数组或集合中元素的数据类型,变量名表示在每次循环中获取到的元素值的变量名(自己定义),数组/集合表示需要遍历的数组或集合对象。
当代码执行到for循环时,系统会自动将数组或集合中的每一个元素取出,并赋值给变量名,循环体中的代码就可以使用变量名来访问元素的值。每次循环结束后,都会自动获取下一个元素,直到遍历完整个数组或集合为止。
需要注意的是,增强for循环只能用于遍历数组和实现了Iterable接口的集合类,例如List和Set,对于Map集合需要使用其他方法遍历。另外,增强for循环不能修改数组或集合中的元素。
3.6.2 增强for实例
下面是一个使用增强for循环遍历数组和集合的示例代码:
// 遍历数组
int[] arr = {1, 2, 3, 4, 5};
for (int num : arr) {
System.out.println(num);
}
// 遍历List集合
List<String> list = new ArrayList<>();
list.add("apple");
list.add("banana");
list.add("orange");
for (String fruit : list) {
System.out.println(fruit);
}
在以上示例代码中,首先定义了一个数组和一个List集合,然后使用增强for循环遍历它们。在循环体中,使用变量名访问数组或集合中的元素值,并将其输出到控制台。
输出结果如下:
1
2
3
4
5
apple
banana
orange
3.7 List集合子类比较
3.7.1 ArrayList
ArrayList是Java集合框架中的一种常用数据结构,它是基于数组实现的动态数组,可以实现随机访问和快速插入/删除操作。ArrayList具有以下特点:
- 实现了List接口,因此支持有序、可重复的元素集合。
- 内部使用数组存储元素,大小可以根据需要动态地调整。
- 支持任意类型的对象,包括基本数据类型的包装类。
- 提供了类似数组的随机访问方式,可以在O(1)的时间复杂度内访问任意元素。
- 实现了Iterable接口,因此可以使用增强for循环遍历元素。
ArrayList与数组相比具有更高的灵活性和易用性,可以方便地进行动态调整以适应不同的数据需求。它是Java编程中集合框架中最基本、最常用的数据结构之一,也是Java程序员必须掌握的技能之一。
底层数据结构是数组 查询快 增删慢
3.7.2 LinkedList
LinkedList是一种数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。LinkedList可以用来存储和操作任意类型的数据,它的插入和删除操作比数组更高效,因为它不需要移动其他元素来维护索引顺序。
LinkedList可以分为单向链表和双向链表两种类型,单向链表每个节点只有一个指针指向下一个节点,而双向链表每个节点有两个指针,一个指向前一个节点,一个指向后一个节点。双向链表相比单向链表可以更快地进行反向遍历。
LinkedList的一些常见操作包括添加元素到链表头或尾、删除链表中的元素、查找链表中的元素、遍历链表等等。因为LinkedList没有固定大小的限制,所以它通常被用来实现队列、栈、哈希表等数据结构。
底层数据结构是链表 查询慢 增删快
3.7.3 实例
分别使用普通for循环、增强for循环、迭代器进行集合元素的遍历。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
//ArrayList: 底层数据结构是数组 查询快 增删慢
//LinkedList: 底层数据结构是链表 查询慢 增删快
public class test {
public static void main(String[] args) {
//基于数组的 集合ArrayList
ArrayList<String> al = new ArrayList<String>();
//向集合中添加数据
al.add("hello");
al.add("world");
al.add("java");
//增强for循环遍历集合元素
for (String arrlist : al) {
System.out.println(arrlist);
}
System.out.println("-------");
//迭代器遍历集合元素
Iterator<String> it = al.iterator();
for (int i = 0; i < al.size(); i++) {
if (it.hasNext()) {
String next = it.next();
System.out.println(next);
}
}
System.out.println("-------");
//普通for循环遍历集合元素
for (int j = 0; j < al.size(); j++) {
System.out.println(al.get(j));
}
System.out.println("-------");
//基于链表的 集合LinkedList
LinkedList<String> ll = new LinkedList<String>();
ll.add("hello");
ll.add("world");
ll.add("java");
//增强for循环遍历集合元素
for (String llist : ll) {
System.out.println(llist);
}
System.out.println("-------");
//迭代器遍历集合元素
Iterator<String> it2 = ll.iterator();
for (int i = 0; i < al.size(); i++) {
if (it2.hasNext()) {
String next = it2.next();
System.out.println(next);
}
}
System.out.println("-------");
//普通for循环遍历集合元素
for (int j = 0; j < ll.size(); j++) {
System.out.println(ll.get(j));
}
}
}
hello
world
java
-------
hello
world
java
-------
hello
world
java
-------
hello
world
java
-------
hello
world
java
-------
hello
world
java
3.7.4 使用ArrayList集合存储学生信息
创建实体类
public class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建测试类
import java.util.ArrayList;
import java.util.Iterator;
//三种方式遍历学生信息
public class test {
public static void main(String[] args) {
ArrayList<Student> arrlist = new ArrayList<Student>();
Student s1 = new Student("梅西",33);
Student s2 = new Student("C罗",34);
Student s3 = new Student("内马尔",31);
arrlist.add(s1);
arrlist.add(s2);
arrlist.add(s3);
//增强for循环
for (Student stu:arrlist){
System.out.println(stu.getName()+","+stu.getAge());
}
System.out.println("-----------");
//普通for循环
for (int i = 0;i<arrlist.size();i++){
System.out.println(arrlist.get(i).getName()+","+arrlist.get(i).getAge());
}
System.out.println("-----------");
//迭代器
Iterator<Student> iterator = arrlist.iterator();
while(iterator.hasNext()){
Student next = iterator.next();
System.out.println(next.getName()+","+next.getAge());
}
}
}
梅西,33
C罗,34
内马尔,31
-----------
梅西,33
C罗,34
内马尔,31
-----------
梅西,33
C罗,34
内马尔,31
3.7.5 LinkedList集合的特有功能
public void addFirst(E e):在该列表开头插入指定的元素
public void addLast(E e):将指定的元素追加到此列表的末尾
public E getFirst():返回此列表中的第一个元素
public E getLast():返回此列表中的最后一个元素
public E removeFirst():从此列表中删除并返回第一个元素
public E removeLast():从此列表中删除并返回最后一个元素
import java.util.LinkedList;
public class test {
public static void main(String[] args) {
LinkedList<String> ll = new LinkedList<>();
ll.add("hello");
ll.add("world");
ll.add("java");
// public void addFirst(E e):在该列表开头插入指定的元素
ll.addFirst("梅西");
//public void addLast(E e):将指定的元素追加到此列表的末尾
ll.addLast("C罗");
//public E getFirst():返回此列表中的第一个元素
System.out.println(ll.getFirst());
//public E getLast():返回此列表中的最后一个元素
System.out.println(ll.getLast());
//public E removeFirst():从此列表中删除并返回第一个元素
System.out.println(ll.removeFirst());
//public E removeLast():从此列表中删除并返回最后一个元素
System.out.println(ll.removeLast());
System.out.println(ll);
}
}
梅西
C罗
梅西
C罗
[hello, world, java]
4. Set集合
4.1 Set集合概述
Java中的Set集合是一种无序、不可重复的数据集合,它继承自Collection接口,是其中的一种实现类。
Set集合中的元素不能重复,因此可以用来去重。同时,Set的实现方式也决定了它具有一些特殊的性质,例如:
-
元素是无序的:Set中的元素没有任何顺序,遍历时不能保证元素的顺序。
-
元素不能重复:Set集合中的元素不允许重复,若添加一个已经存在的元素,则会覆盖原有元素。
Java中Set集合主要有三种实现类:
-
HashSet:基于哈希表实现,插入、删除、查找效率都很高,但是迭代顺序不固定。
-
TreeSet:基于红黑树实现,插入、删除、查找的效率比HashSet略低,但是元素会按照大小顺序排序。
-
LinkedHashSet:具有HashSet的查询速度,内部使用链表维护元素的插入顺序,因此可以按照插入顺序遍历元素。
4.2 Set集合的常用方法
-
add(E e):将元素e添加到集合中,若集合中已经存在该元素,则不添加。
-
remove(Object o):将集合中的元素o移除,若元素不存在则不做任何操作。
-
contains(Object o):判断集合中是否包含元素o,若包含则返回true,否则返回false。
-
isEmpty():判断集合是否为空,若为空则返回true,否则返回false。
-
size():返回集合中元素的个数。
4.2.1 add(E e)
这是一个基本的Set集合添加元素的代码示例:
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("cherry");
set.add("apple"); // 将不会被添加到set中
System.out.println(set);
}
}
输出结果:
[banana, cherry, apple]
在这个示例中,我们创建了一个HashSet对象,然后向集合中添加了四个元素:apple、banana、cherry和apple。由于集合中不能包含重复元素,所以第二个apple不会被添加到set中。
最后,我们打印了set集合中的元素,输出结果表明,集合中只有三个元素:apple、banana和cherry。
4.2.2 remove(Object o)
以下是使用Set集合的remove方法从集合中移除元素的示例代码:
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("cherry");
System.out.println("原集合:" + set);
// 从集合中移除元素"banana"
boolean removed = set.remove("banana");
if (removed) {
System.out.println("移除成功,移除后集合:" + set);
} else {
System.out.println("元素不存在,不做任何操作,集合:" + set);
}
// 移除不存在的元素"pear"
removed = set.remove("pear");
if (removed) {
System.out.println("移除成功,移除后集合:" + set);
} else {
System.out.println("元素不存在,不做任何操作,集合:" + set);
}
}
}
输出结果:
原集合:[banana, cherry, apple]
移除成功,移除后集合:[cherry, apple]
元素不存在,不做任何操作,集合:[cherry, apple]
在此示例中,我们首先创建了一个HashSet对象,并向其中添加三个元素:apple、banana和cherry。然后我们调用了remove方法来从集合中移除元素"banana"。由于该元素存在于集合中,所以remove方法返回true,我们在输出语句中打印了移除后集合的内容。
接着,我们尝试移除集合中不存在的元素"pear"。由于该元素不存在于集合中,所以remove方法返回false,我们在输出语句中打印了元素不存在的提示信息。
最后,我们打印了最终的集合内容,输出结果表明,集合中只剩下了cherry和apple两个元素。
4.2.3 contains(Object o)
以下是使用Set集合的contains方法来判断集合中是否包含某个元素的示例代码:
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("cherry");
// 判断集合中是否包含元素"banana"
if (set.contains("banana")) {
System.out.println("集合中包含元素\"banana\"");
} else {
System.out.println("集合中不包含元素\"banana\"");
}
// 判断集合中是否包含元素"pear"
if (set.contains("pear")) {
System.out.println("集合中包含元素\"pear\"");
} else {
System.out.println("集合中不包含元素\"pear\"");
}
}
}
输出结果:
集合中包含元素"banana"
集合中不包含元素"pear"
在此示例中,我们首先创建了一个HashSet对象,并向其中添加三个元素:apple、banana和cherry。然后我们调用了contains方法来判断集合中是否包含元素"banana"和"pear"。由于"banana"存在于集合中,因此第一个判断语句输出包含的信息;而"pear"不存在于集合中,因此第二个判断语句输出不包含的信息。
4.2.4 isEmpty()
以下是使用Set集合的isEmpty方法来判断集合是否为空的示例代码:
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set1 = new HashSet<String>();
// 判断集合set1是否为空
if (set1.isEmpty()) {
System.out.println("集合set1为空");
} else {
System.out.println("集合set1不为空");
}
Set<String> set2 = new HashSet<String>();
set2.add("apple");
set2.add("banana");
set2.add("cherry");
// 判断集合set2是否为空
if (set2.isEmpty()) {
System.out.println("集合set2为空");
} else {
System.out.println("集合set2不为空");
}
}
}
输出结果:
集合set1为空
集合set2不为空
在此示例中,我们首先创建了一个空的HashSet对象set1,并使用isEmpty方法判断它是否为空。由于set1为空,因此输出集合set1为空的信息。然后我们创建了一个包含三个元素的HashSet对象set2,并再次使用isEmpty方法判断它是否为空。由于set2不为空,因此输出集合set2不为空的信息。
4.2.5 size()
以下是使用Set集合的size方法来获取集合中元素个数的示例代码:
import java.util.HashSet;
import java.util.Set;
public class SetDemo {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("apple");
set.add("banana");
set.add("cherry");
// 获取集合set的元素个数,并输出
int size = set.size();
System.out.println("集合set中的元素个数为:" + size);
}
}
输出结果:
集合set中的元素个数为:3
在此示例中,我们首先创建了一个包含三个元素的HashSet对象set,并使用size方法获取其元素个数,并将结果保存到变量size中。最后输出变量size的值。
4.3 哈希值
4.3.1 哈希值概述
在Java中,哈希值是通过将对象的内容映射为数字来计算的。哈希值的主要作用是:
- 在哈希表中快速查找对象。
- 判断两个对象是否相等。
在Java中,每个对象都有一个默认的哈希值,可以通过调用对象的hashCode()方法来获取。hashCode()方法返回一个int类型的整数,用于表示该对象的哈希码。哈希码的计算可以基于对象的内容、属性等信息,具体取决于对象本身的实现。如果两个对象相等,则它们的哈希码必须相等;反之,如果两个对象的哈希码不等,则它们不一定不相等。
Java中的许多集合类,如HashMap、HashSet、LinkedHashSet等,都是基于哈希表实现的,它们在查找、添加、删除、判重等操作中都会使用哈希值。因此,对于实现了哈希码的Java对象,它们可以更高效地被这些集合类所利用。
4.3.2哈希值实例
创建实体类
我们可以创建并操作学生的对象。
public class Student {
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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 "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
测试类
public class test {
public static void main(String[] args) {
/*
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
同一个对象多次使用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法,可以实现让不同的哈希值相同。
*/
Student s = new Student("梅西",33);
Student c = new Student("C罗",34);
// 同一个对象多次使用hashCode()方法返回的哈希值是相同的
System.out.println(c.hashCode());
System.out.println(c.hashCode());
System.out.println(s.hashCode());
}
}
1163157884
1163157884
1956725890
4.4 HashSet
4.4.1 HashSet集合概述
HashSet是Java中实现集合接口的一个类,它是基于哈希表的实现方式,继承于AbstractSet,实现了Set接口,也可以看做是一个集合框架中的一种。它具有以下特点:
-
无序性:HashSet不保证元素的迭代顺序。特别地,它不保证这个顺序恒久不变。所以不能使用for循环进行遍历。
-
唯一性:在HashSet中,不允许重复元素。如果试图将一个元素加入HashSet中,而这个元素已经存在于集合中,那么这个 add 操作将会被忽略。
-
高效性:HashSet的实现方式是基于哈希表的,因此可以快速地进行插入、删除、查找等操作。对于大数据量的操作,HashSet的效率远高于List(动态数组)等其他集合类。
-
线程不安全性:HashSet不是线程安全的。在多线程环境下使用时,应该进行同步处理。
总之,HashSet是一个不允许重复元素的集合,可以高效地进行插入、删除、查找等操作,但是不保证元素的迭代顺序,并且在多线程环境下需要进行同步处理。
import java.util.HashSet;
public class hashsettest {
public static void main(String[] args) {
HashSet<String> hashSet = new HashSet<>();
hashSet.add("hello");
hashSet.add("world");
hashSet.add("hello");
for (String h:hashSet){
System.out.println(h);
}
}
}
world
hello
4.4.2 HashSet集合存储学生信息
下面是一个使用HashSet存储学生信息的实例,包括实体类和测试类:
//实体类
public 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;
}
//保证HashSet的唯一性 需要进行 对 hashCode 和 equals的 重写
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
Student student = (Student) obj;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
// 测试类
import java.util.HashSet;
import java.util.Set;
public class TestHashSet {
public static void main(String[] args) {
Set<Student> students = new HashSet<>();
students.add(new Student("张三", 18));
students.add(new Student("李四", 19));
students.add(new Student("王五", 20));
students.add(new Student("张三", 18)); // 重复元素,不会添加到集合中
for (Student student : students) {
System.out.println(student.getName() + " " + student.getAge());
}
}
}
以上代码中,TestHashSet类中创建了一个HashSet集合,并通过add方法向集合中添加了4个Student对象,其中有2个元素是相同的。由于HashSet不允许出现重复元素,因此只会保留其中的3个元素。遍历HashSet集合时,将输出三个学生对象。
4.5 LinkedHashSet
4.5.1 LinkedHashSet集合概述
LinkedHashSet是一个基于哈希表和链表实现的集合类,继承自HashSet类。它和HashSet一样,不允许元素重复,但是存储的元素是有序的,即按照元素添加的顺序排列。
LinkedHashSet的主要特点如下:
-
有序性:元素按照添加的顺序排列;
-
唯一性:不允许元素重复;
-
高效性:使用哈希表实现,查找速度快;
-
线程不安全:LinkedHashSet类是非同步的。即多个线程同时访问LinkedHashSet对象时,可能会产生不可预测的结果。
因为它兼具了HashSet和LinkedHashMap的特征,所以不仅拥有HashSet去重的优点,还能保证顺序。在某些场景下,LinkedHashSet能提供更好的性能表现。
import java.util.LinkedHashSet;
public class test {
public static void main(String[] args) {
LinkedHashSet<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("hello");
linkedHashSet.add("world");
linkedHashSet.add("hello");
for (String lhs : linkedHashSet) {
System.out.println(lhs);
}
}
}
4.6 TreeSet
4.6.1 TreeSet集合概述
TreeSet是一个基于红黑树实现的集合类,它实现了SortedSet接口,能够自动按照元素的自然排序规则进行排序,或者根据指定的Comparator进行排序。TreeSet不允许存储重复元素。
TreeSet的主要特点如下:
-
自动排序:TreeSet会根据元素的自然排序规则或指定的Comparator进行排序;
-
唯一性:TreeSet不允许存储重复元素;
-
高效性:使用红黑树进行实现,基于树形结构,查找、插入、删除操作的时间复杂度都为O(logn);
-
线程不安全:TreeSet类是非同步的,即多个线程同时访问TreeSet对象时,可能会产生不可预测的结果。
由于TreeSet能够自动排序,适用于需要排序的场景。但是由于其基于树形结构实现,可能会占用较大的内存空间,因此在存储大量元素时,需要考虑到其内存占用情况。
import java.util.TreeSet;
public class test245 {
public static void main(String[] args) {
//集合中要求是 引用的数据类型 所以存储整数需要用到Integer 而不是 int 对于String的引用数据类型就是其本身
TreeSet<Integer> treeSet = new TreeSet<>();
treeSet.add(20);
treeSet.add(10);
treeSet.add(40);
treeSet.add(30);
for (Integer t : treeSet) {
System.out.println(t);
}
}
}
4.7 TreeSet集合排序
4.7.1 自然排序Comparable
下面是一个使用Java自然排序进行排序的示例,示例中使用了Set集合和Comparable接口的compareTo方法:
import java.util.Set;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
Set<Person> set = new TreeSet<>();
set.add(new Person("Tom", 25));
set.add(new Person("Jack", 30));
set.add(new Person("Mary", 20));
for (Person person : set) {
System.out.println(person);
}
}
}
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
return this.age - o.age; // 按照年龄从小到大排序
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
输出结果:
Person{name='Mary', age=20}
Person{name='Tom', age=25}
Person{name='Jack', age=30}
在这个示例中,我们定义了一个Person类,并实现了Comparable接口的compareTo方法,该方法返回的是根据对象的年龄进行比较的结果,然后将Person对象加入到了Set集合中。由于Set集合要求元素是唯一的,因此通过使用compareTo方法来实现自然排序,保证了Set集合中不会出现年龄相同的对象。最终输出的结果是根据年龄从小到大排序的。
4.7.2 比较器排序Comparator
实体类
public class Comparator{
String name;
int age;
public Comparator() {
}
public Comparator(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 "Comparator{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
如果年纪相同按照姓名根据哈希表排序
import java.util.Comparator;
import java.util.TreeSet;
public class test {
public static void main(String[] args) {
TreeSet<Comparator> treeSet = new TreeSet<Comparator>(new Comparator<Comparator>() {
@Override
public int compare(Comparator s1, Comparator s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Comparators1 = new Comparator("messi", 33);
Comparators2 = new Comparator("neimaer", 30);
Comparators3 = new Comparator("lamosi", 32);
Comparators4 = new Comparator("cluo", 32);
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
for (Comparator s : treeSet) {
System.out.println(s);
}
}
}
Student247{name='neimaer', age=30}
Student247{name='cluo', age=32}
Student247{name='lamosi', age=32}
Student247{name='messi', age=33}
4.7.3 排序实例
根据成绩进行排序
实体类
public class Student248 {
String name;
int yuwen;
int shuxue;
public Student248(String name, int yuwen, int shuxue) {
this.name = name;
this.yuwen = yuwen;
this.shuxue = shuxue;
}
public Student248() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getYuwen() {
return yuwen;
}
public void setYuwen(int yuwen) {
this.yuwen = yuwen;
}
public int getShuxue() {
return shuxue;
}
public void setShuxue(int shuxue) {
this.shuxue = shuxue;
}
public int sum(){
return this.shuxue + this.yuwen;
}
@Override
public String toString() {
return "Student248{" +
"name='" + name + '\'' +
", yuwen=" + yuwen +
", shuxue=" + shuxue +
'}';
}
}
测试类
import java.util.Comparator;
import java.util.TreeSet;
public class Test248 {
public static void main(String[] args) {
//创建TreeSet集合 并且加入匿名内部类来进行排序
TreeSet<Student248> treeSet = new TreeSet<>(new Comparator<Student248>() {
@Override
public int compare(Student248 s1, Student248 s2) {
//按照总分 从下到大排序
int num = s1.sum() - s2.sum();
//如果总分相同 按照语文成绩从小到大排序
int num2 = num == 0 ? s1.getYuwen() - s2.getYuwen() : num;
//如果总分和语文成绩都相同 按照姓名根据哈希表来进行排序
int num3 = num2 == 0 ? s1.getName().compareTo(s2.getName()) : num2;
return num3;
}
});
//创建学生类对象,向其中添加信息
Student248 s1 = new Student248("C罗", 99, 100);
Student248 s2 = new Student248("梅西", 90, 88);
Student248 s3 = new Student248("内马尔", 100, 99);
Student248 s4 = new Student248("拉莫斯", 94, 78);
Student248 s5 = new Student248("莫德里奇", 94, 78);
//将学生信息加入集合中
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
treeSet.add(s5);
//增强for循环遍历集合数据
for (Student248 s : treeSet) {
System.out.println("姓名:" + s.getName() + ",数学成绩:" + s.getShuxue() + ",语文成绩:" + s.getYuwen() + ",总成绩:" + s.sum());
}
}
}
姓名:拉莫斯,数学成绩:78,语文成绩:94,总成绩:172
姓名:莫德里奇,数学成绩:78,语文成绩:94,总成绩:172
姓名:梅西,数学成绩:88,语文成绩:90,总成绩:178
姓名:C罗,数学成绩:100,语文成绩:99,总成绩:199
姓名:内马尔,数学成绩:99,语文成绩:100,总成绩:199
5. 泛型
5.1 泛型概述
Java泛型是一种在编译时期进行类型检查和类型推断的机制。它可以在类、接口、方法和集合中使用。
泛型的作用是可以让代码更加通用化、类型安全,同时可以减少类型转换的操作。使用泛型可以避免在程序运行期间发生类型错误,增强了程序的可读性、可维护性和代码重用性。
在Java中,泛型的语法是通过在类名或方法名后面添加尖括号(<>)来实现的。在尖括号中声明泛型类型,可以是任意合法的Java标识符,通常使用“T”、“E”、“K”、“V”等字母表示泛型类型。例如:
5.1.1 泛型类
/*
修饰符 class 类名<类型>{}
private class Generic<E>{}
*/
public class Box<E> {
private E e;
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
}
/*
泛型类 相当于一个模板
*/
public class Test251 {
public static void main(String[] args) {
//定义字符串类型的泛型类对象
Box<String> g1 = new Box<String>();
g1.setE("梅西");
System.out.println(g1.getE());
//定义整数类型的泛型类对象
Box<Integer> g2 = new Box<Integer>();
g2.setE(22);
System.out.println(g2.getE());
//定义布尔类型的泛型类对象
Box<Boolean> g3 = new Box<Boolean>();
g3.setE(true);
System.out.println(g3.getE());
}
}
梅西
22
true
在这个例子中,泛型类型是“E”,表示这个Box类可以存放任意类型的对象。
使用泛型的好处是,在编译时期可以检查类型的正确性,从而避免在运行时期出现类型转换错误;同时,代码中也不再需要进行类型转换,使得代码更加简洁和安全。
5.1.2 泛型方法
泛型方法是一种在方法中使用泛型类型的方式。它与泛型类类似,可以在方法的定义中使用泛型类型作为参数、返回值类型或者方法中的局部变量类型。泛型方法使得方法可以独立于任何具体类型,提高了代码的灵活性和可重用性。
在泛型方法中,泛型类型参数可以用来表示任何类型,比如:类、接口、数组、枚举等。泛型方法中的类型参数在方法调用时由编译器确定其具体类型,而不是在方法定义时确定。
泛型方法与普通方法的最大区别在于其具有更强的类型检查和更高的代码重用性。泛型方法能够使方法更加灵活和通用,能够处理不同类型的输入参数,并以原始类型为基础来工作。泛型方法在 Java 中被广泛应用,比如:集合类、数组工具类、排序算法等等。
public class fangfa {
public <T> void show(T t){
System.out.println(t);
}
}
public class Test {
public static void main(String[] args) {
fangfa f = new fangfa();
f.show("梅西");
f.show(22);
f.show(true);
}
}
5.1.3 泛型接口
泛型接口是指在接口定义时使用泛型类型参数,使得实现这个接口的类可以在实现时确定这个泛型类型的具体类型。通过这样做,可以在编译时进行类型检查,并且在运行时避免类型转换的错误。
泛型接口也可以实现一些通用的数据结构或算法,使得代码的重用性更高,并且可以减少代码的重复编写。它常用于集合类和通用算法的实现中。
Java中的泛型接口是通过在接口定义时加上类型参数来实现的:
interface MyInterface<T> {
T getValue();
void setValue(T value);
}
实现泛型接口时,需要传递具体的类型参数:
class MyClass implements MyInterface<String> {
private String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
实例
通过多态的方式实现
public interface fanxing<T> {
void show(T t);
}
重写接口定义的方法
public class fanixngImpl <T> implements fanxing<T>{
public void show(T t){
System.out.println(t);
}
}
测试类
public class fanxingTest {
public static void main(String[] args) {
fanxing<String> f1 = new fanixngImpl<String>();
f1.show("梅西");
fanxing<Integer> f2 = new fanixngImpl<Integer>();
f2.show(22);
fanxing<Boolean> f3 = new fanixngImpl<Boolean>();
f3.show(true);
}
}
5.2 可变参数
可变类型参数是一种允许方法接受可变数量的参数的语法特性。在 Java 中,可变类型参数使用 ...
表示。
可变类型参数的语法格式:
public void methodName(Type... parameterName) {
//方法体
}
在调用可变类型参数方法时,可以传入任意数量的参数,Java 会将这些参数封装成一个数组作为方法的参数。例如:
public void printNumbers(int... numbers) {
for (int number : numbers) {
System.out.println(number);
}
}
printNumbers(1, 2, 3); // 输出:1 2 3
printNumbers(new int[]{1, 2, 3}); // 输出:1 2 3
在上面的例子中,printNumbers
方法接受可变数量的 int
类型参数,当我们传入参数时,可以直接传入一组值,也可以传入一个数组。
需要注意的是,可变类型参数必须放在参数列表的最后一个位置,因为方法在调用时,会优先将参数传递给与参数类型相匹配的方法,如果可变类型参数不放在最后一个位置,那么在调用方法时,就无法判断哪些参数属于可变类型参数了。
可变参数实例
public class Test {
public static void main(String[] args) {
System.out.println(sum(10,20));
System.out.println(sum(10, 20,20));
System.out.println(sum(10, 20,20,90));
}
public static int sum(int a, int b) {
return a + b;
}
public static int sum(int... a) {
int ss = 0;
for (int s : a) {
ss = ss + s;
}
return ss;
}
}
30
50
140
6. Map集合
6.1 Map集合概述
Map 是 Java 中的一个接口,它提供了一种映射关系,将一组键值对关联起来。Map 中的每一个元素都是一个键值对,其中键是唯一的,而值则可以重复。
Map 中常用的实现类包括 HashMap、TreeMap、LinkedHashMap,它们都实现了 Map 接口,并提供了不同的实现方式。
public class Test257 {
/*
Interface Map(K,V) k:键的类型 V:值的类型
将键映射到值的对象,不能包含重复的键,一个键最多映射一个值
创建Map集合对象
多态的方式
具体的实现类 HashMap
*/
public static void main(String[] args) {
//创建 Map集合 依托于 HashMap
Map<String, String> map = new HashMap<String, String>();
//Map集合 添加数据用的是 put()方法
map.put("heima01", "梅西");
map.put("heima02", "C罗");
//当二者的键的数据相同时,后者会将前者的数据进行覆盖
//这里也体现了Map集合的唯一性 主要是因为HashMap中的哈希表来保证的
map.put("heima03", "内马尔");
map.put("heima03", "拉莫斯");
System.out.println(map);
}
}
6.2 Map集合常用方法
6.2.1 put(K key,V value)
下面是一个使用 Map 的 put(K key,V value) 方法的示例代码:
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
// 创建 Map 对象
Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("apple", 10);
map.put("banana", 20);
map.put("orange", 30);
// 输出 Map 对象
System.out.println("Map: " + map);
}
}
输出结果为:
Map: {banana=20, orange=30, apple=10}
在上述代码中,我们首先创建了一个 Map 对象,并使用 put(K key,V value) 方法向该 Map 中添加元素,其中键是字符串类型,值是整数类型。最后,使用 System.out.println() 方法输出整个 Map 对象。
注意,Map 中的元素是没有顺序的,因此输出的顺序可能与添加的顺序不同。
6.2.2 remove (Object key)
下面是一个使用 Map 的 remove(Object key) 方法的示例代码:
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
// 创建 Map 对象
Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("apple", 10);
map.put("banana", 20);
map.put("orange", 30);
// 输出 Map 对象
System.out.println("Before remove: " + map);
// 删除元素
map.remove("banana");
// 输出 Map 对象
System.out.println("After remove: " + map);
}
}
输出结果为:
Before remove: {banana=20, orange=30, apple=10}
After remove: {orange=30, apple=10}
在上述代码中,我们使用 put(K key,V value) 方法向 Map 中添加元素,然后使用 remove(Object key) 方法删除键为 "banana" 的元素。最后,使用 System.out.println() 方法输出删除元素后的 Map 对象。
注意,remove(Object key) 方法返回被删除的键对应的值,如果不存在该键,则返回 null。如果需要判断某个键是否存在,可以使用 containsKey(Object key) 方法。
6.2.3 clear()
以下是使用clear()方法清空Map集合的代码示例:
import java.util.HashMap;
import java.util.Map;
public class MapClearExample {
public static void main(String[] args) {
// 创建Map集合
Map<String, Integer> map = new HashMap<>();
// 添加键值对
map.put("Java", 1);
map.put("Python", 2);
map.put("C++", 3);
// 打印Map集合
System.out.println("Map集合清空前:" + map);
// 清空Map集合
map.clear();
// 打印Map集合
System.out.println("Map集合清空后:" + map);
}
}
输出结果为:
Map集合清空前:{Java=1, Python=2, C++=3}
Map集合清空后:{}
在上面的示例中,我们创建了一个HashMap实例,添加了3个键值对,然后使用clear()方法将Map集合清空。
6.2.4 containsKey(Object key)
以下是使用containsKey()方法判断Map集合中是否包含指定键的代码示例:
import java.util.HashMap;
import java.util.Map;
public class MapContainsKeyExample {
public static void main(String[] args) {
// 创建Map集合
Map<String, String> map = new HashMap<>();
// 添加键值对
map.put("name", "Tom");
map.put("age", "18");
map.put("gender", "male");
// 判断是否包含指定键
boolean containsName = map.containsKey("name");
boolean containsEmail = map.containsKey("email");
// 输出结果
System.out.println("Map集合包含键'name': " + containsName);
System.out.println("Map集合包含键'email': " + containsEmail);
}
}
输出结果为:
Map集合包含键'name': true
Map集合包含键'email': false
在上面的示例中,我们创建了一个HashMap实例,添加了3个键值对,然后使用containsKey()方法判断Map集合中是否包含指定键。最后输出结果表明集合中包含键'name',但不包含键'email'
6.2.5 containsValue(Object value)
以下是使用containsValue()方法判断Map集合中是否包含指定值的代码示例:
import java.util.HashMap;
import java.util.Map;
public class MapContainsValueExample {
public static void main(String[] args) {
// 创建Map集合
Map<String, String> map = new HashMap<>();
// 添加键值对
map.put("name", "Tom");
map.put("age", "18");
map.put("gender", "male");
// 判断是否包含指定值
boolean containsTom = map.containsValue("Tom");
boolean containsEmail = map.containsValue("abc@xyz.com");
// 输出结果
System.out.println("Map集合包含值'Tom': " + containsTom);
System.out.println("Map集合包含值'abc@xyz.com': " + containsEmail);
}
}
输出结果为:
Map集合包含值'Tom': true
Map集合包含值'abc@xyz.com': false
在上面的示例中,我们创建了一个HashMap实例,添加了3个键值对,然后使用containsValue()方法判断Map集合中是否包含指定值。最后输出结果表明集合中包含值'Tom',但不包含值'abc@xyz.com'。
6.2.6 isEmpty()
以下是使用isEmpty()方法判断Map集合是否为空的代码示例:
import java.util.HashMap;
import java.util.Map;
public class MapIsEmptyExample {
public static void main(String[] args) {
// 创建Map集合
Map<String, String> map = new HashMap<>();
// 判断是否为空
boolean empty = map.isEmpty();
System.out.println("Map集合是否为空: " + empty);
// 添加键值对
map.put("name", "Tom");
map.put("age", "18");
map.put("gender", "male");
// 判断是否为空
empty = map.isEmpty();
System.out.println("Map集合是否为空: " + empty);
}
}
输出结果为:
Map集合是否为空: true
Map集合是否为空: false
在上面的示例中,我们创建了一个HashMap实例,使用isEmpty()方法判断Map集合是否为空。此时集合为空,输出结果为true。然后我们添加了3个键值对后再次使用isEmpty()方法判断Map集合是否为空。此时集合不为空,输出结果为false。
6.2.7 size()
以下是使用size()方法获取Map集合中键值对数量的代码示例:
import java.util.HashMap;
import java.util.Map;
public class MapSizeExample {
public static void main(String[] args) {
// 创建Map集合
Map<String, String> map = new HashMap<>();
// 添加键值对
map.put("name", "Tom");
map.put("age", "18");
map.put("gender", "male");
// 获取集合长度
int size = map.size();
System.out.println("Map集合中键值对数量: " + size);
}
}
输出结果为:
Map集合中键值对数量: 3
在上面的示例中,我们创建了一个HashMap实例,并使用put()方法添加了3个键值对。然后使用size()方法获取Map集合中键值对的数量,输出结果为3。
6.3 Map集合获取
采用三种不同的方法获取 集合中的元素
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Map<String,String> map = new HashMap<String,String>();
map.put("梅西","球王");
map.put("C罗","跳水王子");
map.put("内马尔","巴西第一深情");
//使用get方法 通过键的值来获取对应值的值 如果键不存在 会输出 null
System.out.println(map.get("梅西"));
System.out.println(map.get("拉莫斯"));
System.out.println("----------------");
//通过keySet()方法获取 所有键的值 因为键是唯一的 不能重复 所以用Set<String> 集合类型
Set<String> sm = map.keySet();
for (String key:sm){
System.out.println(key);
}
System.out.println("-----------------");
//通过value()方法获取 所有值的值 因为值不是唯一的 可以重复 所以用Collection<String> 集合类型
Collection<String> cm = map.values();
for (String value:cm){
System.out.println(value);
}
}
}
球王
null
----------------
C罗
梅西
内马尔
-----------------
跳水王子
球王
巴西第一深情
6.4Map集合遍历
6.4.1 方法1
通过KeySet()方法获取所有的键,通过增强for循环 得到相对应的值。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
//创建Map集合
Map<String, String> map = new HashMap<String, String>();
//向集合中添加数据
map.put("梅西", "球王");
map.put("C罗", "跳水王子");
map.put("内马尔", "巴西第一深情");
//通过KeySet()方法获取所有的键
Set<String> sm = map.keySet();
for (String key : sm) {
//根据键 的到相对应的值
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
C罗,跳水王子
梅西,球王
内马尔,巴西第一深情
6.4.2 方法2
获取所有的键值对对象集合,之后对集合进行遍历 得到每一个键值对的值
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
//创建Map集合
Map<String, String> map = new HashMap<String, String>();
//向集合中添加数据
map.put("梅西", "球王");
map.put("C罗", "跳水王子");
map.put("内马尔", "巴西第一深情");
//获取所有键值对对象的集合
Set<Map.Entry<String,String>> sme = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String,String> kv:sme){
//根据得到的 键值对对象 获得相应的 键的值和值的值
System.out.println(kv.getKey() + "," + kv.getValue());
}
}
}
C罗,跳水王子
梅西,球王
内马尔,巴西第一深情
6.4.3 Map集合存储球员信息
创建实体类 包含球员姓名和年龄
public class Student {
String name;
int age;
public Student (String name, int age) {
this.name = name;
this.age = age;
}
public Student () {
}
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 "StudentHashSet{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
创建测试类
采用两种不同的方法遍历集合元素
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test {
public static void main(String[] args) {
//创建Map集合
Map<String, Student> map = new HashMap<String, Student>();
//创建学生类对象 向其中添加数据
Students1 = new Student("侯晋悄", 22);
Students2 = new Student("梅西", 33);
Students3 = new Student("C罗", 34);
Students4 = new Student("C罗", 34);
//向集合中添加学生信息
map.put("heima001", s1);
map.put("heima002", s2);
map.put("heima003", s3);
map.put("heima004", s4);
//获得所有的键
Set<String> sm = map.keySet();
//遍历所有的键 来获得相对应的值
for (String key : sm) {
Student262 value = map.get(key);
System.out.println(key + "," + value.getName() + "," + value.getAge());
}
System.out.println("------------------");
//获得所有的键值对
Set<Map.Entry<String, Student262>> entries = map.entrySet();
//遍历所有的键值对 来得到相对应的键 和 值
for (Map.Entry<String, Student262> kv : entries) {
String key = kv.getKey();
Student262 value = kv.getValue();
System.out.println(key + "," + value.getName() + "," + value.getAge());
}
}
}
heima001,侯晋悄,22
heima003,C罗,34
heima002,梅西,33
heima004,C罗,34
------------------
heima001,侯晋悄,22
heima003,C罗,34
heima002,梅西,33
heima004,C罗,34
6.4.4 键是球员信息时存储
实体类
保证HashMap的唯一性 需要进行 对 hashCode 和 equals的 重写
import java.util.Objects;
public class Student {
String name;
int age;
public Student () {
}
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 String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student that = (Student) o;
return age == that.age && Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
测试类
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
HashMap<Student,String> hm = new HashMap<Student,String>();
Student s1 = new Student("梅西",33);
Student s2 = new Student("内马尔",33);
Student s3 = new Student("莫德里奇",33);
Student s4 = new Student("莫德里奇",33);
hm.put(s1,"阿根廷");
hm.put(s2,"巴西");
hm.put(s3,"克罗地亚");
hm.put(s4,"法国");
Set<Student> keySet = hm.keySet();
for (Student key:keySet){
String value = hm.get(key);
System.out.println(key.getName()+","+key.getAge()+","+value);
}
}
}
梅西,33,阿根廷
内马尔,33,巴西
莫德里奇,33,法国
7. 集合嵌套
Java集合嵌套是指在一个集合中嵌套了另一个集合,可以是同类型集合,也可以是不同类型集合。
常用的Java集合类中,如List、Set和Map都可以嵌套其他集合,经常使用集合嵌套的场景包括:
-
一个List集合中存储多个Map集合,每个Map集合表示一个订单,包含订单号、下单时间、商品信息等。
-
一个Map集合中存储多个List集合,每个List集合表示一个学生的考试成绩,包含语文、数学、英语等科目的成绩。
-
一个Set集合中存储多个Set集合,每个Set集合表示一个班级,包含多个学生信息。
集合嵌套可以更好地组织和管理数据,提高代码的可读性和可维护性。
7.1 ArrayList集合嵌套HashMap集合
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
public class test {
public static void main(String[] args) {
//创建ArrayList集合 泛型的类型是 HashMap集合类型 HashMap集合的键值都是String类型
ArrayList<HashMap<String, String>> arrayList = new ArrayList<HashMap<String, String>>();
//创建HashMap集合
HashMap<String, String> hashMap1 = new HashMap<String, String>();
//将元素添加进 HashMap集合
hashMap1.put("梅西", "迈阿密国际");
hashMap1.put("C罗", "利雅得胜利");
//将HashMap集合作为元素 添加到ArrayList集合中
arrayList.add(hashMap1);
//创建HashMap集合
HashMap<String, String> hashMap2 = new HashMap<String, String>();
//将元素添加进 HashMap集合
hashMap2.put("内马尔", "利雅得新月");
hashMap2.put("本泽马", "吉达联合");
//将HashMap集合作为元素 添加到ArrayList集合中
arrayList.add(hashMap2);
//创建HashMap集合
HashMap<String, String> hashMap3 = new HashMap<String, String>();
//将元素添加进 HashMap集合
hashMap3.put("莫德里奇", "皇家马德里");
hashMap3.put("德保罗", "马德里竞技");
//将HashMap集合作为元素 添加到ArrayList集合中
arrayList.add(hashMap3);
//遍历HashMap集合
for (HashMap<String, String> hashMap : arrayList) {
//先找到所有的键
Set<String> keySet = hashMap.keySet();
//遍历所有的键 通过键找到相对应的值
for (String key : keySet) {
String value = hashMap.get(key);
//输出集合元素
System.out.println(key + "," + value);
}
}
}
}
C罗,利雅得胜利
梅西,迈阿密国际
内马尔,利雅得新月
本泽马,吉达联合
德保罗,马德里竞技
莫德里奇,皇家马德里
该代码创建了一个ArrayList集合,其中每个元素都是一个HashMap集合,HashMap集合中的键和值都是String类型。
然后分别创建三个HashMap集合,每个HashMap集合中存储了两个键值对。接着将这三个HashMap集合作为元素添加到ArrayList集合中。
最后使用双重循环遍历ArrayList集合中的每一个HashMap集合。对于每个HashMap集合,使用keySet()方法获取所有的键,然后再通过键获取相应的值,并进行输出。
7.2 HashMap集合嵌套ArrayList集合
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;
public class test {
public static void main(String[] args) {
//创建HashMap集合 键的类型是 String 值的类型是 ArrayList 集合 ArrayList的类型的String
HashMap<String, ArrayList<String>> hashMap = new HashMap<String, ArrayList<String>>();
//创建一个类型是String类型的 ArrayList集合
ArrayList<String> arrayList1 = new ArrayList<String>();
//向HashMap集合中添加String类型的数据
arrayList1.add("梅西");
arrayList1.add("迪玛利亚");
//向HashMap集合中添加 ArrayList集合类型的数据
hashMap.put("阿根廷", arrayList1);
//创建一个类型是String类型的 ArrayList集合
ArrayList<String> arrayList2 = new ArrayList<String>();
//向HashMap集合中添加String类型的数据
arrayList2.add("C罗");
arrayList2.add("菲利克斯");
//向HashMap集合中添加 ArrayList集合类型的数据
hashMap.put("葡萄牙", arrayList2);
//创建一个类型是String类型的 ArrayList集合
ArrayList<String> arrayList3 = new ArrayList<String>();
//向HashMap集合中添加String类型的数据
arrayList3.add("罗纳尔迪尼奥");
arrayList3.add("维尼修斯");
//向HashMap集合中添加 ArrayList集合类型的数据
hashMap.put("巴西", arrayList3);
//找到所有的键
Set<String> keySet = hashMap.keySet();
//遍历所有的键
for (String key : keySet) {
//通过键找到相对应的值
ArrayList<String> value = hashMap.get(key);
System.out.println(key);
for (String al : value) {
//遍历值
System.out.println("\t" + al);
}
}
}
}
巴西
罗纳尔迪尼奥
维尼修斯
葡萄牙
C罗
菲利克斯
阿根廷
梅西
迪玛利亚
该代码实现了一个 HashMap 集合中嵌套 ArrayList 集合的场景,通过键值对的形式存储了每个国家对应的足球明星列表。
首先创建了一个 HashMap 集合,键的类型是 String,值的类型是 ArrayList<String>,表示每个国家对应的足球明星列表。
然后创建了三个 ArrayList<String> 集合分别表示阿根廷、葡萄牙和巴西的足球明星列表,向这三个 ArrayList 集合中添加了对应国家的足球明星名字。
接着将这三个国家和对应的 ArrayList<String> 集合作为键值对分别添加到 HashMap 集合中。
最后通过遍历 HashMap 集合中的键和值,输出每个国家的足球明星列表。
8. 小结
在我的学习过程中,我参考了各大平台的资源,尤其要感谢黑马的视频教程,为我提供了宝贵的学习资料。我将这些学习笔记进行整理和加工,并将它们发布到CSDN上。这些笔记主要记录了我在学习过程中的经验和心得,我希望能与同样在这条学习路上的同学和朋友们分享。
在学习集合框架时,我了解了Java中常用的集合类,如List、Set和Map,并学会了它们的特点和用法。List是一种有序的集合,可以存储重复的元素;Set是一种无序的集合,不允许重复元素的存在;而Map则是一种键值对的集合,可以通过键来获取对应的值。
我学习了集合框架中的各种操作方法,如添加元素、删除元素、查询元素等。同时,我也了解了集合框架的遍历方式,包括使用迭代器和增强型for循环来遍历集合中的元素。此外,我还学习了如何使用集合框架提供的排序和比较功能,以及如何进行集合的转换和复制操作。
除了基本的集合框架,我还深入学习了ArrayList、LinkedList、HashSet、TreeSet和HashMap等具体的实现类。对于每种实现类,我了解了其内部数据结构和性能特点,以及适用的使用场景。我还学习了集合框架中的线程安全问题,并了解了如何使用同步控制来确保多线程环境下的安全访问。
通过这些学习,我对Java集合框架有了更深入的理解,并能够灵活运用它们解决实际问题。我相信,通过我的学习笔记的分享,能够帮助到其他同学和朋友们更好地掌握和应用集合框架的知识。