Java三十天速成(java进阶篇)

server/2025/2/6 16:13:20/

Day 15

一、集合框架

1、集合框架图

2、数据结构:

①、逻辑结构;

在计算机科学中,逻辑结构是指数据元素之间的关系,它描述了数据元素之间的逻辑联系,而不是它们在计算机内存中的物理存储方式。逻辑结构可以分为以下三种类型:

  1. 线性结构:线性结构是一种有序的数据结构,其中数据元素按照线性顺序排列,每个元素都有一个前驱和后继。常见的线性结构包括数组、链表、栈和队列等。

  2. 非线性结构:非线性结构是一种无序的数据结构,其中数据元素之间没有明确的前驱和后继关系。常见的非线性结构包括树和图等。树是一种层结构,其中每个节点有一个父节点和零个或多个子节点。图是一种由节点和边组成的集合,其中节点表示对象,边表示节点之间的关系。

  3. 集合结构:集合结构是一种无序的数据结构,其中数据元素之间没有明确的前驱和后继关系,也没有层次结构。**常见的集合结构包括散列表和集合等。**散列表是一种基于哈希函数实现的数据结构,它可以快速地查找、插入和删除数据元素。集合是一种无序的数据结构,其中每个元素都是唯一的。

②、物理存储结构

在计算机科学中,物理存储结构是指数据在计算机内存中的实际存储方式。常见的物理存储结构包括以下三种类型:

  1. 顺序存储结构:顺序存储结构是一种将数据元素按照顺序存储在一段连续的内存空间中的方式。在顺序存储结构中,每个数据元素占用相同的存储空间,可以通过下标来访问元素。常见的顺序存储结构包括数组和线性表等。

  2. 链式存储结构:链式存储结构是一种将数据元素存储在不连续的内存空间中的方式。在链式存储结构中,每个数据元素包含一个数据域和一个指针域,指针域指向下一个元素的地址。常见的链式存储结构包括单链表、双向链表和循环链表等。

  3. 索引存储结构:索引存储结构是一种将数据元素存储在不连续的内存空间中的方式,同时还维护了一个索引表,用于快速访问数据元素。在索引存储结构中,索引表中的每个元素包含一个关键字和一个指针,指针指向对应数据元素的地址。常见的索引存储结构包括B树和B+树等。

3、collection接口:

①、在Java集合框架中,Collection是一个接口,它代表一组对象,这些对象被称为元素。Collection接口提供了一些通用的方法,例如添加元素、删除元素、判断元素是否存在、获取元素数量等等。它是所有集合类的根接口,包括List、Set和Queue等。

List是一个有序的集合,它允许重复元素。List接口提供了一些特定的方法,例如按索引访问元素、在指定位置插入元素、替换指定位置的元素等等。常见的List实现类有ArrayList和LinkedList。

Set是一个不允许重复元素的集合,它保证元素的唯一性。Set接口提供了一些特定的方法,例如添加元素、删除元素、判断元素是否存在等等。常见的Set实现类有HashSet和TreeSet。

Queue是一个队列,它按照一定的顺序存储元素,并提供了一些特定的方法,例如添加元素、删除元素、获取队列头部元素等等。常见的Queue实现类有LinkedList和PriorityQueue。

总之,Collection接口和它的实现类提供了一些非常有用的数据结构,可以帮助我们更方便地管理和操作一组对象。

②、Collection接口是Java集合框架中所有集合类的根接口,它提供了一些通用的方法,包括:

③练习:

import java.util.List;
import java.util.Objects;public class test4 {public static void main(String[] args) {//动态数组List list = new ArrayList();ArrayList list1 = new ArrayList();list1.add("A");list1.add("B");list1.add("C");list1.add(5.1);System.out.println(list1.size());for (Object s:list1){System.out.println(s);}System.out.println(list1.get(3));}
}
out:
4
A
B
C
5.1
5.1

上面代码中list1中存放时存在小数类型,这样会在实际中造成一些不必要的麻烦,因此我们需要限制list1中只能放一种类型;

package test.test;import java.util.ArrayList;
import java.util.List;
import java.util.Objects;public class test4 {public static void main(String[] args) {//动态数组List<String> list = new ArrayList<String>();list.add("A");list.add("B");list.add("C");list.add(5.1);   //会报错:java: 不兼容的类型: double无法转换为java.lang.Stringlist.add("D");System.out.println(list.size());for (Object s:list){System.out.println(s);}System.out.println(list.get(3));}
}
将list.add(5.1); 注释后:
out:
4
A
B
C
D
D
4、泛型:
泛型是jdk1.5的一种新特性,是一种语法糖(语法糖:看起来很美好,其实不存在)本质上只存留在语法层面,一旦编译成字节码后,泛型就要被擦除掉,还原成Object类型状态。所以java中的泛型是一种语法糖,伪泛型;

泛型本质上是一种形式化参数,它允许在编译时指定集合中元素的类型,从而提高代码的类型安全性和可读性。泛型可以应用于类、接口、方法等,它的核心思想是参数化类型。

①、泛型的定义方法:

在Java泛型中,使用尖括号<>来指定类型参数,例如List表示一个只能存储字符串类型的List集合。在定义泛型类或方法时,可以使用任意标识符来表示类型参数,例如、、等等。

public class test4 {public static void main(String[] args) {A<String,Integer> a = new A<String,Integer>();}
}
//类型泛型参数
class A<C,T>{}
//方法泛型参数
class B{public <C> void test(C a){}
}

②、泛型的优点包括:

  1. 提高代码的类型安全性:泛型可以在编译时检查类型,避免了在运行时出现类型转换异常的情况。

  2. 提高代码的可读性:泛型可以让代码更加清晰明了,减少了类型转换的代码。

  3. 提高代码的重用性:泛型可以应用于不同类型的集合,提高了代码的重用性。

③、补充:

在Java中,Integer是一个包装类,它封装了一个基本数据类型int的值,并提供了一些有用的方法来操作这个值。Integer类是final的,因此不能被继承。

Integer类提供了许多方法来操作int类型的值,例如:

  1. intValue():将Integer对象转换为int类型的值。

  2. compareTo(Integer anotherInteger):比较两个Integer对象的值,如果当前对象小于anotherInteger,则返回负数;如果当前对象等于anotherInteger,则返回0;如果当前对象大于anotherInteger,则返回正数。

  3. parseInt(String s):将字符串s解析为int类型的值。

  4. valueOf(int i):返回一个Integer对象,它封装了指定的int类型的值。

  5. toString():将Integer对象转换为字符串类型的值。

  6. equals(Object obj):比较当前对象和另一个对象是否相等。

  7. hashCode():返回当前对象的哈希码值。

  8. MAX_VALUE:表示int类型的最大值。

  9. MIN_VALUE:表示int类型的最小值。

需要注意的是,由于Integer是一个包装类,因此它可以作为参数传递给方法,也可以作为返回值返回。在Java中,自动装箱和拆箱机制可以自动地将int类型的值转换为Integer对象,或将Integer对象转换为int类型的值,使得使用Integer对象更加方便。

④、作业:

加入购物车时候,如果购物车中已经存在相关商品,购物车商品购买数量累计+1

import java.util.HashMap;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;class ShoppingCart {private Map<String, Integer> items;public ShoppingCart() {items = new HashMap<>();}public void addItem(String itemName) {if (items.containsKey(itemName)) {int currentQuantity = items.get(itemName);items.put(itemName, currentQuantity + 1);} else {items.put(itemName, 1);}}public Map<String, Integer> getItems() {return items;}public static void main(String[] args) {ShoppingCart cart = new ShoppingCart();cart.addItem("苹果");cart.addItem("香蕉");cart.addItem("苹果");cart.addItem("橙子");cart.addItem("苹果");Map<String, Integer> items = cart.getItems();for (String itemName : items.keySet()) {int quantity = items.get(itemName);System.out.println(itemName + ": " + quantity);}}
}
out:
苹果: 3
香蕉: 1
橙子: 1

Day 16

一、Iterable/Collection/List接口:

1、Vector:

①什么是Vector:

Vector是Java中的一种动态数组,它可以自动扩展以容纳任意数量的元素。与数组不同的是,Vector可以动态地添加或删除元素,而不需要手动调整数组大小。

②、语法:

Vector的使用方法与数组类似,可以使用索引来访问元素,也可以使用add、remove等方法来添加或删除元素。以下是一个使用Vector的示例:

import java.util.Vector;public class Example {public static void main(String[] args) {// 创建一个新的VectorVector<String> vector = new Vector<>();// 向Vector中添加元素vector.add("apple");vector.add("banana");vector.add("orange");// 访问Vector中的元素String element = vector.get(1);System.out.println(element); // 输出:banana// 删除Vector中的元素vector.remove(2);// 遍历Vector中的元素for (String str : vector) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的Vector,并向其中添加了三个元素。我们使用get方法访问了Vector中的第二个元素,并使用remove方法删除了第三个元素。最后,我们使用for循环遍历了Vector中的所有元素。

需要注意的是,Vector是线程安全的,因为它的所有方法都是同步的。如果不需要线程安全的特性,建议使用ArrayList,因为它的性能更好。

2、ArrayList:

①什么是ArrayList:

ArrayList是Java中的一种动态数组,它可以自动扩展以容纳任意数量的元素。与数组不同的是,ArrayList可以动态地添加或删除元素,而不需要手动调整数组大小。

②、语法:

ArrayList的使用方法与数组类似,可以使用索引来访问元素,也可以使用add、remove等方法来添加或删除元素。以下是一个使用ArrayList的示例:

import java.util.ArrayList;public class Example {public static void main(String[] args) {// 创建一个新的ArrayListArrayList<String> list = new ArrayList<>();// 向ArrayList中添加元素list.add("apple");list.add("banana");list.add("orange");// 访问ArrayList中的元素String element = list.get(1);System.out.println(element); // 输出:banana// 删除ArrayList中的元素list.remove(2);// 遍历ArrayList中的元素for (String str : list) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的ArrayList,并向其中添加了三个元素。我们使用get方法访问了ArrayList中的第二个元素,并使用remove方法删除了第三个元素。最后,我们使用for循环遍历了ArrayList中的所有元素。

需要注意的是,ArrayList是非线程安全的,因此在多线程环境下使用ArrayList可能会导致数据不一致的问题。如果需要线程安全的特性,可以考虑使用Vector或者Collections.synchronizedList方法来创建一个线程安全的ArrayList。

3、LinkedList:

①什么是

LinkedList是Java中的一种链表数据结构,它可以用来存储任意类型的对象。与ArrayList不同的是,LinkedList的元素是通过节点连接起来的,而不是存储在连续的内存块中。

②、语法:

LinkedList的使用方法与ArrayList类似,可以使用add、remove等方法来添加或删除元素,也可以使用get方法来访问元素。以下是一个使用LinkedList的示例:

import java.util.LinkedList;public class Example {public static void main(String[] args) {// 创建一个新的LinkedListLinkedList<String> list = new LinkedList<>();// 向LinkedList中添加元素list.add("apple");list.add("banana");list.add("orange");// 访问LinkedList中的元素String element = list.get(1);System.out.println(element); // 输出:banana// 删除LinkedList中的元素list.remove(2);// 遍历LinkedList中的元素for (String str : list) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的LinkedList,并向其中添加了三个元素。我们使用get方法访问了LinkedList中的第二个元素,并使用remove方法删除了第三个元素。最后,我们使用for循环遍历了LinkedList中的所有元素。

需要注意的是,由于LinkedList的元素是通过节点连接起来的,因此访问LinkedList中的元素可能比访问ArrayList中的元素慢。另外,LinkedList不支持随机访问,因为它没有像ArrayList那样的索引。如果需要随机访问元素,建议使用ArrayList。

二、Iterable/Collection/Queue接口:

1、Deque:

①什么是

Deque是Java中的一种双端队列数据结构,它可以在队列的两端添加或删除元素。Deque是一个接口,Java提供了两个Deque的实现类:ArrayDeque和LinkedList。

②、语法:

Deque的使用方法与Queue类似,可以使用add、remove、peek等方法来添加、删除和访问元素。另外,Deque还提供了一些特殊的方法,如addFirst、addLast、removeFirst、removeLast等方法,用于在队列的两端添加或删除元素。以下是一个使用Deque的示例:

import java.util.ArrayDeque;
import java.util.Deque;public class Example {public static void main(String[] args) {// 创建一个新的DequeDeque<String> deque = new ArrayDeque<>();// 在队列的尾部添加元素deque.add("apple");deque.add("banana");deque.add("orange");// 在队列的头部添加元素deque.addFirst("pear");// 在队列的尾部删除元素deque.removeLast();// 遍历Deque中的元素for (String str : deque) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的ArrayDeque,并向其中添加了三个元素。我们使用addFirst方法在队列的头部添加了一个元素,使用removeLast方法在队列的尾部删除了一个元素。最后,我们使用for循环遍历了Deque中的所有元素。

需要注意的是,ArrayDeque是一个基于数组的Deque实现,它的性能比LinkedList更好。如果需要在Deque的两端频繁地添加或删除元素,建议使用ArrayDeque。如果需要在Deque中间插入或删除元素,建议使用LinkedList。

三、Iterable/Collection/Set接口:

1、TreeSet

①什么是TreeSet:

TreeSet是Java中的一种基于红黑树实现的有序集合,它可以存储任意类型的对象,并且保证集合中的元素是有序的。TreeSet中的元素必须实现Comparable接口,或者在创建TreeSet时提供一个Comparator比较器。

②语法:

TreeSet的使用方法与Set类似,可以使用add、remove等方法来添加或删除元素,也可以使用contains方法来判断集合中是否包含某个元素。以下是一个使用TreeSet的示例:

import java.util.TreeSet;public class Example {public static void main(String[] args) {// 创建一个新的TreeSetTreeSet<String> set = new TreeSet<>();// 向TreeSet中添加元素set.add("apple");set.add("banana");set.add("orange");// 判断TreeSet中是否包含某个元素boolean contains = set.contains("banana");System.out.println(contains); // 输出:true// 删除TreeSet中的元素set.remove("orange");// 遍历TreeSet中的元素for (String str : set) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的TreeSet,并向其中添加了三个元素。我们使用contains方法判断TreeSet中是否包含某个元素,并使用remove方法删除了一个元素。最后,我们使用for循环遍历了TreeSet中的所有元素。

需要注意的是,由于TreeSet是基于红黑树实现的,因此它的插入、删除和查找操作的时间复杂度都是O(log n),其中n是集合中的元素个数。另外,由于TreeSet是有序的,因此它的遍历操作是按照元素的顺序进行的。如果需要一个有序的集合,并且需要频繁地进行插入、删除和查找操作,建议使用TreeSet。

2、HashSet:

①什么是HashSet:

HashSet是Java中的一种无序集合,它可以存储任意类型的对象,并且保证集合中的元素是唯一的。HashSet使用哈希表实现,因此插入、删除和查找操作的时间复杂度都是O(1)。

②语法:

HashSet的使用方法与Set类似,可以使用add、remove等方法来添加或删除元素,也可以使用contains方法来判断集合中是否包含某个元素。以下是一个使用HashSet的示例:

import java.util.HashSet;public class Example {public static void main(String[] args) {// 创建一个新的HashSetHashSet<String> set = new HashSet<>();// 向HashSet中添加元素set.add("apple");set.add("banana");set.add("orange");// 判断HashSet中是否包含某个元素boolean contains = set.contains("banana");System.out.println(contains); // 输出:true// 删除HashSet中的元素set.remove("orange");// 遍历HashSet中的元素for (String str : set) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的HashSet,并向其中添加了三个元素。我们使用contains方法判断HashSet中是否包含某个元素,并使用remove方法删除了一个元素。最后,我们使用for循环遍历了HashSet中的所有元素。

需要注意的是,由于HashSet是无序的,因此它的遍历操作是不保证元素的顺序的。如果需要一个无序的集合,并且需要频繁地进行插入、删除和查找操作,建议使用HashSet。

Day 17

一、Map接口:

1、TreeMap:

①什么是TreeMap:

TreeMap是Java中的一种基于红黑树实现的有序映射,它可以存储键值对,并且保证键是有序的。TreeMap中的键必须实现Comparable接口,或者在创建TreeMap时提供一个Comparator比较器。

②、语法:

TreeMap的使用方法与Map类似,可以使用put、remove等方法来添加或删除键值对,也可以使用get方法来访问键对应的值。以下是一个使用TreeMap的示例:

import java.util.TreeMap;public class Example {public static void main(String[] args) {// 创建一个新的TreeMapTreeMap<String, Integer> map = new TreeMap<>();// 向TreeMap中添加键值对map.put("apple", 1);map.put("banana", 2);map.put("orange", 3);// 访问TreeMap中的键对应的值int value = map.get("banana");System.out.println(value); // 输出:2// 删除TreeMap中的键值对map.remove("orange");// 遍历TreeMap中的键值对for (Map.Entry<String, Integer> entry : map.entrySet()) {String key = entry.getKey();int value = entry.getValue();System.out.println(key + ": " + value);}}
}

在这个示例中,我们创建了一个新的TreeMap,并向其中添加了三个键值对。我们使用get方法访问TreeMap中的一个键对应的值,并使用remove方法删除了一个键值对。最后,我们使用for循环遍历了TreeMap中的所有键值对。

需要注意的是,由于TreeMap是基于红黑树实现的,因此它的插入、删除和查找操作的时间复杂度都是O(log n),其中n是映射中的键值对个数。另外,由于TreeMap是有序的,因此它的遍历操作是按照键的顺序进行的。如果需要一个有序的映射,并且需要频繁地进行插入、删除和查找操作,建议使用TreeMap。

③补充(红黑树):

红黑树是一种自平衡的二叉搜索树,它的每个节点都有一个颜色,可以是红色或黑色。红黑树满足以下性质:

  1. 每个节点要么是红色,要么是黑色。

  2. 根节点是黑色的。

  3. 每个叶子节点(NIL节点,空节点)是黑色的。

  4. 如果一个节点是红色的,则它的两个子节点都是黑色的。

  5. 对于每个节点,从该节点到其所有后代叶子节点的简单路径上,均包含相同数目的黑色节点。

红黑树的插入、删除和查找操作的时间复杂度都是O(log n),其中n是树中节点的个数。红黑树的自平衡性质保证了树的高度不会超过log n,因此这些操作的时间复杂度是O(log n)。

红黑树常用于实现有序映射(如TreeMap)和有序集合(如TreeSet)等数据结构。

2、HashMap:

①什么是HashMap:

HashMap是一种编程中的数据结构,它允许高效地存储和检索键值对。通常它被实现为一个链表数组,其中数组中的每个元素都是一个桶,可以容纳多个键值对。

要将值存储在HashMap中,首先需要将键哈希化以确定值应存储的桶的索引。如果该桶中已经存储了值,则新值将添加到链表的末尾。

要从HashMap中检索值,需要将键哈希化以确定应该定位值的桶的索引。然后在该桶中的链表中搜索键,如果找到,则返回相应的值。

②、语法:

import java.util.HashMap;public class Example {public static void main(String[] args) {// 创建一个新的HashMapHashMap<String, Integer> map = new HashMap<>();// 向HashMap中添加键值对map.put("apple", 1);map.put("banana", 2);map.put("orange", 3);// 从HashMap中检索值int value = map.get("banana");System.out.println(value); // 输出:2}
}

在这个示例中,我们创建了一个新的HashMap,将字符串映射到整数。我们向HashMap中添加了三个键值对,然后检索与键“banana”相关联的值。程序的输出是2,这是我们为键“banana”添加的值。

HashMap中的键必须是唯一的,如果尝试将一个已经存在的键添加到HashMap中,它将会覆盖原有的值。值可以重复,因此可以将多个键映射到同一个值。

HashMap在Java中是线程不安全的,如果需要在多线程环境下使用HashMap,可以考虑使用ConcurrentHashMap或者在访问HashMap时进行同步处理。

3、Hashtable

①什么是Hashtable

Hashtable是Java中另一种用于存储键值对的数据结构,它与HashMap类似,但是具有一些不同的特点。

Hashtable也是一个哈希表,它使用键的哈希码来确定值的存储位置。与HashMap不同的是,Hashtable是线程安全的,因为它的所有方法都是同步的。这意味着多个线程可以同时访问Hashtable,而不会导致数据不一致的问题。

②、语法:

Hashtable的使用方法与HashMap类似,它也提供了put、get、remove、containsKey等方法。以下是一个使用Hashtable的示例:

import java.util.Hashtable;public class Example {public static void main(String[] args) {// 创建一个新的HashtableHashtable<String, Integer> table = new Hashtable<>();// 向Hashtable中添加键值对table.put("apple", 1);table.put("banana", 2);table.put("orange", 3);// 从Hashtable中检索值int value = table.get("banana");System.out.println(value); // 输出:2}
}

需要注意的是,由于Hashtable是线程安全的,因此在多线程环境下使用Hashtable可能会导致性能问题。如果不需要线程安全的特性,建议使用HashMap。

练习:

信息管理系统对外提供信息检索,信息录入等功能,如教务系统,提供学生信息录入,学生信息查询,为了防止非本校人员侵入系统,破坏系统数据,教务系统都有安全检查机制 如必须登录才能访问,那么信息系统如何防止没有登录的非法人员,在得知学生编号,但不知道学生密码情况,越过登录窗口访问呢,大多数的系统,都是在系统内部配置一个缓存,每次登录成功的用户都把用户信息存入缓存,当有人操作系统信息时候 先去判断缓存中,是否有当前用户,如果有继续检索返回数据 如果没有当前用户 ,拒绝访问,请根据我的描述,利用面向对象思想,设计相关类并且测试

package test.test.xinxi;
import java.awt.image.ImageProducer;
import java.util.HashMap;
import java.util.Map;public class test1 {public static void main(String[] args) {// 创建学生信息管理系统Cache cache = new Cache();SecurityCheck securityCheck = new SecurityCheck(cache);InformationSystem informationSystem = new InformationSystem(securityCheck);// 添加学生信息informationSystem.addInformation(new User("user1", "password1"), "学生1的信息");// 登录User user = new User("user1", "password1");securityCheck.login(user);// 检索学生信息System.out.println(informationSystem.retrieveInformation(user, "1"));// 登出securityCheck.logout(user);}
}//User类:表示系统的用户,包含用户名和密码)。
class User {private String username;private String password;public User(String username, String password) {this.username = username;this.password = password;}public String getUsername() {return username;}public String getPassword() {return password;}
}
//Cache类:包含添加和删除用户的方法,以及检查用户是否已登录。
class Cache {private Map<String, User> loggedInUsers;public Cache() {loggedInUsers = new HashMap<>();}public void addUser(User user) {loggedInUsers.put(user.getUsername(), user);}public void removeUser(User user) {loggedInUsers.remove(user.getUsername());}public boolean isLoggedIn(User user) {return loggedInUsers.containsKey(user.getUsername());}
}
//SecurityCheck类:表示用于确保只有已登录用户才能访问系统的安全检查机制。包含对缓存的引用以及检查用户是否已登录和登录用户的方法。
class SecurityCheck {private Cache cache;public SecurityCheck(Cache cache) {this.cache = cache;}public boolean isLoggedIn(User user) {return cache.isLoggedIn(user);}public void login(User user) {cache.addUser(user);}public void logout(User user) {cache.removeUser(user);}
}
//InformationSystem类:表示信息管理系统,包含检索和添加信息的方法。还包含对安全检查机制的引用,以确保只有已登录用户才能访问系统。
class InformationSystem {private SecurityCheck securityCheck;public InformationSystem(SecurityCheck securityCheck) {this.securityCheck = securityCheck;}public String retrieveInformation(User user, String studentId) {if (securityCheck.isLoggedIn(user)) {// Retrieve informationreturn "学生" + studentId + "的信息";} else {return "拒绝访问,请先登录。";}}public void addInformation(User user, String information) {if (securityCheck.isLoggedIn(user)) {// Add informationSystem.out.println("添加信息:" + information);} else {System.out.println("拒绝访问,请先登录。");}}
}
输出:
拒绝访问,请先登录。
学生1的信息

二、比较器:

1、比较器(Comparator):

比较器(Comparator)是Java中的一个接口,它定义了一种比较两个对象大小的方法。比较器可以用于对集合中的元素进行排序,或者在需要比较对象大小的其他场合使用。

比较器有两种使用方式:

  1. 实现Comparator接口,重写compare方法,然后将比较器对象传递给集合的排序方法。

  2. 实现Comparable接口,重写compareTo方法,然后直接对集合中的元素进行排序。

2、示例:

以下是一个使用Comparator接口对集合中的元素进行排序的示例:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;public class Example {public static void main(String[] args) {// 创建一个新的ListList<String> list = new ArrayList<>();// 向List中添加元素list.add("apple");list.add("banana");list.add("orange");// 使用Comparator接口对List中的元素进行排序Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}});// 遍历List中的元素for (String str : list) {System.out.println(str);}}
}

在这个示例中,我们创建了一个新的List,并向其中添加了三个元素。我们使用Comparator接口对List中的元素进行排序,然后使用for循环遍历了List中的所有元素。由于我们使用的是默认的比较器,因此List中的元素按照字典序进行了排序。

需要注意的是,如果需要对集合中的元素进行排序,可以使用Collections.sort方法。如果集合中的元素实现了Comparable接口,那么可以直接调用Collections.sort方法进行排序。如果集合中的元素没有实现Comparable接口,那么需要提供一个Comparator比较器来进行排序。

Day 18

一、多线程:

1、什么是线程?什么是进程?

线程和进程都是计算机中的执行单元,但它们有一些重要的区别。

进程是一个正在运行的程序的实例。它包括程序代码、数据和执行状态。每个进程都有自己的内存空间和系统资源,如文件句柄、网络连接和进程优先级。进程之间是相互独立的,它们不能直接访问彼此的内存空间和资源。

线程是进程中的一个执行单元。一个进程可以包含多个线程,每个线程都可以独立执行不同的任务。线程共享进程的内存空间和系统资源,因此它们可以更快地通信和协作。线程之间的切换比进程之间的切换更快,因为线程不需要切换内存空间和系统资源。

总的来说,进程和线程都是计算机中的执行单元,但它们的区别在于进程是一个独立的执行环境,而线程是在进程内部的执行单元。

2、什么是并行?什么是并发?

并行和并发都是多任务处理的概念,但它们有一些重要的区别。

并行是指同时执行多个任务,每个任务都在不同的处理器核心或计算机上执行。这意味着多个任务可以同时完成,从而提高了系统的吞吐量和性能。

并发是指同时执行多个任务,但这些任务可能会共享同一个处理器核心或计算机资源。这意味着多个任务可以交替执行,从而创建了一种错觉,好像它们是同时执行的。并发通常用于处理I/O密集型任务,如网络通信和文件操作。

总的来说,并行是指同时执行多个任务,而并发是指交替执行多个任务。并行通常用于处理计算密集型任务,如图像处理和科学计算,而并发通常用于处理I/O密集型任务,如网络通信和文件操作。

3、创建线程的几种常见方式?

在Java中,创建线程的方式有以下几种:

①、继承Thread类:可以创建一个继承Thread类的子类,并重写run()方法来定义线程的执行逻辑。

class MyThread extends Thread {public void run() {System.out.println("Thread is running");}
}// 创建线程
MyThread t = new MyThread();// 启动线程
t.start();

②、实现Runnable接口:可以创建一个实现Runnable接口的类,并将其作为参数传递给Thread类的构造函数。

class MyRunnable implements Runnable {public void run() {System.out.println("Thread is running");}
}// 创建线程
MyRunnable r = new MyRunnable();
Thread t = new Thread(r);// 启动线程
t.start();

③、用匿名内部类:可以使用匿名内部类来创建线程。

// 创建线程
Thread t = new Thread(new Runnable() {public void run() {System.out.println("Thread is running");}
});// 启动线程
t.start();

④、使用Lambda表达式:可以使用Lambda表达式来创建线程。

// 创建线程
Thread t = new Thread(() -> {System.out.println("Thread is running");
});// 启动线程
t.start();

总的来说,Java中创建线程的方式比较灵活,可以根据具体的需求选择不同的方式。无论使用哪种方式,都需要注意线程安全和资源管理等问题,以确保程序的正确性和性能。

4、线程同步如何实现?

在Java中,线程同步可以通过以下几种方式来实现:

①、synchronized关键字:可以使用synchronized关键字来实现线程同步。

synchronized关键字可以用来修饰方法或代码块,它会锁定对象或类,使得同一时刻只有一个线程可以访问被锁定的代码。

public synchronized void increment() {count++;
}

②、Lock接口:可以使用Lock接口来实现线程同步。

Lock接口提供了比synchronized关键字更灵活的锁定机制,可以支持更复杂的同步需求。

Lock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}
}

③、volatile关键字:可以使用volatile关键字来实现线程间的可见性。

volatile关键字可以保证变量的值在多个线程之间是可见的,即一个线程修改了变量的值,其他线程可以立即看到这个变化。

private volatile int count = 0;public void increment() {count++;
}

需要注意的是,线程同步可以保证线程安全,但会降低程序的性能。因此,在实际应用中,需要根据具体的需求和场景来选择合适的线程同步机制。同时,还需要注意避免死锁、饥饿等问题,以确保程序的正确性和性能。

5、Java中的各种锁

①、synchronized关键字

public class Counter {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}

在这个示例中,我们使用synchronized关键字来修饰increment()和getCount()方法,从而实现对Counter对象的锁定。这样可以保证同一时刻只有一个线程可以访问increment()和getCount()方法,从而实现线程同步。

②、ReentrantLock类

import java.util.concurrent.locks.ReentrantLock;public class Counter {private int count = 0;private ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}}public int getCount() {return count;}
}

在这个示例中,我们使用ReentrantLock类来实现线程同步。在increment()方法中,我们首先调用lock()方法获取锁,然后执行count++操作,最后调用unlock()方法释放锁。这样可以保证同一时刻只有一个线程可以访问increment()方法,从而实现线程同步。

③、ReadWriteLock接口

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class Counter {private int count = 0;private ReadWriteLock lock = new ReentrantReadWriteLock();public void increment() {lock.writeLock().lock();try {count++;} finally {lock.writeLock().unlock();}}public int getCount() {lock.readLock().lock();try {return count;} finally {lock.readLock().unlock();}}
}

在这个示例中,我们使用ReadWriteLock接口来实现线程同步。在increment()方法中,我们使用writeLock()方法获取写锁,然后执行count++操作,最后使用unlock()方法释放写锁。在getCount()方法中,我们使用readLock()方法获取读锁,然后返回count的值,最后使用unlock()方法释放读锁。这样可以保证多个线程可以同时读取count的值,但只有一个线程可以写入count的值,从而实现线程同步。

④、StampedLock类

import java.util.concurrent.locks.StampedLock;public class Counter {private int count = 0;private StampedLock lock = new StampedLock();public void increment() {long stamp = lock.writeLock();try {count++;} finally {lock.unlockWrite(stamp);}}public int getCount() {long stamp = lock.tryOptimisticRead();int c = count;if (!lock.validate(stamp)) {stamp = lock.readLock();try {c = count;} finally {lock.unlockRead(stamp);}}return c;}
}

在这个示例中,我们使用StampedLock类来实现线程同步。在increment()方法中,我们使用writeLock()方法获取写锁,然后执行count++操作,最后使用unlockWrite()方法释放写锁。在getCount()方法中,我们首先使用tryOptimisticRead()方法获取一个乐观读锁,然后读取count的值。如果在读取count的值时发现锁已经被其他线程占用,则使用readLock()方法获取一个悲观读锁,然后再次读取count的值,最后使用unlockRead()方法释放读锁。这样可以保证多个线程可以同时读取count的值,但只有一个线程可以写入count的值,从而实现线程同步。

6、线程池的几种方式?

①、Executors工厂类

Executors是Java中的一个工厂类,可以用来创建各种类型的线程池。Executors提供了多个静态方法,可以创建不同类型的线程池,例如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。

ExecutorService executor = Executors.newFixedThreadPool(10);

在这个示例中,我们使用Executors工厂类创建了一个固定大小的线程池,大小为10。

②、ThreadPoolExecutor类

ThreadPoolExecutor是Java中的一个线程池类,可以用来创建自定义的线程池。ThreadPoolExecutor包含多个构造方法,可以设置线程池的核心线程数、最大线程数、线程空闲时间、任务队列等参数。

ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));

在这个示例中,我们使用ThreadPoolExecutor类创建了一个线程池,其中核心线程数为10,最大线程数为20,线程空闲时间为60秒,任务队列为ArrayBlockingQueue,大小为100。

③、ForkJoinPool类

ForkJoinPool是Java中的一个特殊线程池类,可以用来执行分治任务。ForkJoinPool包含多个静态方法,可以创建不同类型的线程池,例如commonPool()、newWorkStealingPool()等。

ForkJoinPool pool = new ForkJoinPool();

在这个示例中,我们使用ForkJoinPool类创建了一个默认的线程池。

需要注意的是,线程池的选择应该根据具体的需求和场景来选择适合的方式,并注意线程池的大小、任务队列的大小、线程空闲时间等参数的设置,以确保程序的正确性和性能。

7、简单作业:

某公司人力资源管理系统,负责管理公司员工的所有信息,员工入职后,提供工号(系统自动增长),身份证号、姓名、电话、QQ等信息后,就被录入到员工信息表中,如果员工离职,就会从员工信息表中删除,进入离职员工信息表中。人力管理系统提供对某个员工的查询,对某个部门的所有员工遍历查询功能。根据需求按照面向对象思想设计管理系统类。

原笔记链接:FlowUs 息流 - 新一代生产力工具


http://www.ppmy.cn/server/165451.html

相关文章

Baklib引领内容中台与人工智能技术的创新融合之路

内容概要 在数字化转型的浪潮中&#xff0c;各行业正在面临前所未有的挑战与机遇。内容中台作为一种新的概念&#xff0c;逐渐进入了企业的视野&#xff0c;它不仅是一个技术平台&#xff0c;更是提供了整合和管理内容的新思路。从根本上&#xff0c;内容中台旨在提升企业对信…

安全漏洞扫描与修复系统的高质量技术详解

安全漏洞扫描与修复系统的高质量技术详解 在当今数字化时代&#xff0c;网络安全已成为企业运营和个人隐私保护的重中之重。安全漏洞扫描与修复系统作为网络安全防护的关键环节&#xff0c;扮演着“哨兵”的角色&#xff0c;通过全面检测与及时修复&#xff0c;筑牢软件安全的…

Unity 2D实战小游戏开发跳跳鸟 - 记录显示最高分

上一篇文章中我们实现了游戏的开始界面,在开始界面中有一个最高分数的UI,本文将接着实现记录最高分数以及在开始界面中显示最高分数的功能。 添加跳跳鸟死亡事件 要记录最高分,则需要在跳跳鸟死亡时去进行判断当前的分数是否是最高分,如果是最高分则进行记录,如果低于之前…

Joplin 插件在Vscode中无法显示图片

1.问题 在vscode里面装好joplin插件之后&#xff0c;无法显示图片内容。 粘贴的图片可以再vscode中显示&#xff0c;无法再joplin客户端显示 2.解决方法 这种情况是因为和vscode自带的MD编辑器的预览模式有冲突&#xff0c;或者没用通过专用方式上传图片。 方法一&#xff…

C语言:深入了解指针2(超详细)

1. 指针跟数组相关的知识 在 C 语言里&#xff0c;数组名在大多数表达式中会隐式转换为指向数组首元素的指针。不过&#xff0c;数组名和指针并不完全等同。 1.数组就是数组&#xff0c;是一块连续的空间&#xff0c;是可以存放一个或者多个数组的。 2.指针变量是一个变量&am…

项目练习:重写若依后端报错cannot be cast to com.xxx.model.LoginUser

文章目录 一、情景说明二、解决办法 一、情景说明 在重写若依后端服务的过程中 使用了Redis存放LoginUser对象数据 那么&#xff0c;有存就有取 在取值的时候&#xff0c;报错 二、解决办法 方法1、在TokenService中修改如下 getLoginUser 方法中&#xff1a;LoginUser u…

DS图(中)(19)

文章目录 前言一、图的遍历广度优先遍历深度优先遍历 二、最小生成树Kruskal算法Prim算法两种方法对比 总结 前言 承上启下&#xff0c;我们来学习下图的中篇&#xff01;&#xff01;&#xff01; 一、图的遍历 图的遍历指的是遍历图中的顶点&#xff0c;主要有 广度优先遍历 …

机器学习简介

机器学习&#xff08;Machine Learning&#xff09;是人工智能&#xff08;AI&#xff09;的一个分支&#xff0c;它使计算机系统能够利用数据和算法自动学习和改进其性能。 机器学习是一个不断发展的领域&#xff0c;它正在改变我们与技术的互动方式&#xff0c;并为解决复杂…