初识Java 10-1 集合
目录
泛型和类型安全的集合
基本概念
添加一组元素
打印集合
List
Iterator(迭代器)
本笔记参考自: 《On Java 中文版》
在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着,我们必须能够随时随地创建任意数量的对象。
Java提供了几种方法来持有对象(的引用),其中之一就是数组。数组的优点在于其的高效,但其本身却会受到自身大小固定的制约。实际上,Java.util库提供了一组集合类来解决这一问题,其中包括的基本类List、Set、Queue和Map也被称为容器类。
Java并没有直接为集合提供关键字支持。
泛型和类型安全的集合
在Java 5之前,编译器允许向集合中插入不正确的类型。在举例之前需要先对ArrayList类进行一些必要的说明:
ArrayList是是一种泛型类型,它实现了迭代器(Iterable)、Collection和List等接口。泛型是一种参数化类型的机制,在这种机制下,形参的数据类型也会成为可传输的参数。因为泛型的存在,ArrayList可以存储任何类型的对象,而不需要在使用前进行类型转换。(本段参考林二月er的博客、讯飞星火提供的信息)
ArrayList使用一个数值来查找对应对象。从某种意义上,ArrayList将数值和对象关联起来了。
下例中出现的ArrayList是一个反例,它没有使用泛型:
import java.util.ArrayList;class Apple {private static long counter;private final long id = counter++;public long id() {return id;}
}class Orange {
}public class AppleAndOrangesWithoutGenerics {@SuppressWarnings("unchecked") // 参数unchecked表示只有“unchecked”警告应该被忽略public static void main(String[] args) {ArrayList apples = new ArrayList<>();for (int i = 0; i < 3; i++)apples.add(new Apple());apples.add(new Orange()); // 注意:插入的是一个Orange类型,这本来应该是错误的操作for (Object apple : apples) {((Apple) apple).id(); // 需要强制类型转换,此时Orange只有在运行时才会被检测出来}}}
上述程序的错误不会在编译时被发现,这个报错会出现在运行时:
Apple和Orange唯一的联系在于它们都继承了Object类。但ArrayList持有的也是Object,所以无论是Apple类还是Orange类都能被加入其中(并且不会引发报错)。此时会从ArrayList中取出Object类型的引用,若想使用引用还必须强制类型转换。也正是在运行时,程序尝试将Orange对象转型为Apple时才会发现错误。
若要定义一个持有Apple对象的ArrayList,应该使用:
ArrayList<Apple>
尖括号包围的是类型参数(可以有多个),它指定了集合实例中可以保存的类型。
下面是修改后的例子:
import java.util.ArrayList;public class AppleAndOrangesWithGenerics {public static void main(String[] args) {ArrayList<Apple> apples = new ArrayList<>();for (int i = 0; i < 3; i++)apples.add(new Apple());// 此时,下方的做法就会引发编译时错误// apples.add(new Orange());for (Apple apple : apples)System.out.println(apple.id());}
}
程序执行的结果如下:
上述程序中,出现了语句 ArrayList<Apple> apples = new ArrayList<>(); 。这种语句因为<>符号,也被称为“钻石语法”。在Java 7之前,这种语法的使用会更加复杂:
ArrayList<Apple> apples = new ArrayList<Apple>(); // 需要在表达式的两侧重复写出类型声明
随着类型的深入,上述这种语法会变得越发复杂。因此最后被现在这种更为简便的语法代替了。
泛型的存在使得我们从List(接口)中获取对象时,无须进行转型了。因为List知道它所持有的类型。另外,在泛型的情况下,向上转型也可以生效:
import java.util.ArrayList;class GrannySmith extends Apple {
}class Gala extends Apple {
}class Fuji extends Apple {
}class Braeburn extends Apple {
}public class GenericeAndUpcasting {public static void main(String[] args) {ArrayList<Apple> apples = new ArrayList<>();apples.add(new GrannySmith());apples.add(new Gala());apples.add(new Fuji());apples.add(new Braeburn());for (Apple apple : apples)System.out.println(apple);}
}
程序执行的结果是:
因为向上转型,所有可以向持有Apple对象的集合中,放入Apple的子类型。
上述输出结果中,子对象名字后面的是由无符号十六进制表示的哈希码。
新特性:类型推断和泛型
“局部变量类型推断”也可用于简化涉及泛型的定义:
import java.util.ArrayList;public class GenericTypeInference {void old() {ArrayList<Apple> apples = new ArrayList<>();}void modern() {var apples = new ArrayList<Apple>();}void pitFall() {var apples = new ArrayList<>();apples.add(new Apple());apples.get(0); // 会作为普通的Object类型返回}
}
在modern()方法中,定义右侧使用的是<Apple>,通过这种方式提供信息,使编译器知道如何进行类型推断。
但替代现有的语法时也可能会产生问题,在pitFall()方法中存在着语句
var apples = new ArrayList<>();
尽管这样也可以正常编译,但<>实际上会变为<Object>,这不是我们需要的。当我们想要从apples中提取元素时,它们会作为普通的Object类型返回,而不是具体的Apple类型。
基本概念
Java的集合类库是用来“持有对象”的。从设计上,可以将这个库分为两种概念,这两种概念分别表示库的两个基本接口:
- Collection:一个由单独元素组成的序列,这些元素要符合一条或多条规则。
- Map:一组键值-对象对,使用键来查找值。Map也被称为关联数组(或是字典),它关联了对象与其他对象。
在理想情况下,我们编写的大部分代码都是在和这些接口打交道,只有在创建的时候才需要指名所使用的确切类型。例如,按照这种思想创建一个List:
List<Apple> apples = new ArrayList<>();
和之前不同,ArrayList在这里被向上转型为了List。这样,在我们决定修改实现时,只需要修改创建的地方即可:
List<Apple> apples = new LinkedList<>(); // 将实现修改为LinkedList
因此,通常会创建一个具体类的对象,并将其向上转型为相应的接口,在其余的代码中使用该接口。
但这种方法不会总是行得通,因为有些类会有额外的功能。当我们需要使用到这些额外的功能时,就无法将对象向上转型为更通用的接口了。
---
序列是一种持有一组对象的方法,而Collection就是序列这一概念的一般化。例如:
import java.util.ArrayList;
import java.util.Collection;public class SimpleCollection {public static void main(String[] args) {Collection<Integer> c = new ArrayList<>();for (int i = 0; i < 10; i++)c.add(i); // 发生了自动装箱,向c中添加一个元素for (Integer i : c)System.out.print(i + " ");System.out.println();}
}
程序执行的结果如下:
这个示例只使用了Collection中定义的方法,所以继承自该接口的类的任何对象都可以正常工作。不过,ArrayList是最基本的序列类型。
添加一组元素
java.util中的Arrays和Collection类都包含了一些工具方法,用于向一个Collection中添加一组元素:
- Arrays.asList():可以接受一个数组,或一个使用逗号分隔的元素列表,将其转换为一个List对象。
- Collection.addAll():接受一个Collection对象、一个数组或一个用都逗号分隔的列表,将其中的所有元素都添加到这个Collection中。
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;public class AddingGroups {public static void main(String[] args) {Collection<Integer> collection = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));System.out.println("line 10: " + collection);Integer[] moreInts = { 6, 7, 8, 9, 10 };collection.addAll(Arrays.asList(moreInts)); // 传统的addAll()方法System.out.println("line 14: " + collection);// 下方的这种方式运行速度更快,但无法通过这种方式构建Collection。Collections.addAll(collection, 11, 12, 13, 14, 15);System.out.println("line 18: " + collection);Collections.addAll(collection, moreInts);System.out.println("line 20: " + collection);// 生成一个底层是数组的列表:List<Integer> list = Arrays.asList(16, 17, 18, 19, 20);System.out.println("line 24: " + list);list.set(1, 99); // 可以进行元素的修改:将下标为1的元素值改为99// list.add(21); //运行时会发生报错,因为底层数组是不能调整大小的System.out.println("line 27: " + list);}
}
运行结果如下(line后跟的是行号):
Collection.addAll()的运行速度会快很多。因此,可以先构建一个没有元素的Collection,之后调用Collection.addAll()进行元素输入。
collection.addAll(Arrays.asList(moreInts)); 表示该方法只能接受另一个Collection对象作为参数,使用限制较大。相比之下,Arrays.asList()或Collection.addAll()方法就更加好用了。
可以直接将Arrays.asList()的输出作为一个List进行使用,但这种方式进行的实现,其底层是数组,大小无法改变。
也可以向Collection中添加子类型:
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;class Snow {}class Powder extends Snow {}class Light extends Powder {}class Heavy extends Powder {}class Crusty extends Snow {}class Slush extends Snow {}public class AsListInferecnce {public static void main(String[] args) {List<Snow> snow1 = Arrays.asList(new Crusty(), new Slush(), new Powder());// snow1.add(new Heavy()); // 发生异常List<Snow> snow2 = Arrays.asList(new Light(), new Heavy());// snow2.add(new Slush()); // 发生异常List<Snow> snow3 = new ArrayList<>();Collections.addAll(snow3, new Light(), new Heavy(), new Powder()); // 有一个可变参数列表snow3.add(new Crusty()); // 可以正常操作// 使用显式类型参数说明作为提示List<Snow> snow4 = Arrays.<Snow>asList(new Light(), new Heavy(), new Slush());// snow4.add(new Powder()); // 发生异常}
}
打印集合
数组的打印往往需要借助于Arrays.toString()等方法,但就像之前的例子演示过的那样,集合类不需要这些:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
import java.util.TreeSet;public class PrintingCollections {static Collection fill(Collection<String> collection) {collection.add("鼠");collection.add("猫");collection.add("狗");collection.add("狗");return collection;}static Map fill(Map<String, String> map) {map.put("鼠", "杰瑞");map.put("猫", "汤姆");map.put("狗", "斯派克");map.put("狗", "斯派克");return map;}public static void main(String[] args) {System.out.println(fill(new ArrayList<>()));System.out.println(fill(new LinkedList<>()));System.out.println();System.out.println(fill(new HashSet<>()));System.out.println(fill(new TreeSet<>())); // 按键的升序保存对象System.out.println(fill(new LinkedHashSet<>())); // 按添加顺序保存对象System.out.println();System.out.println(fill(new HashMap<>()));System.out.println(fill(new TreeMap<>())); // 按键的升序进行排序System.out.println(fill(new LinkedHashMap<>())); // 按插入顺序保存键,同时具有HashMap的查找速度}
}
程序执行的结果是:
上述例子演示了Java集合类库的两种主要类型。区别在于集合中的每个“槽”(slot)内持有的条目数:
- Collection:每个槽这只保存一个条目。其中,List以指定顺序保存一组条目;Set中,同样的条目只能加入一个;Queue只能一端进,另一端出。
- Map:每个槽内持有两个对象,即键和与之关联的值。
除此之外,上述例子中ArrayList和LinkedList的区别不仅在于进行一些操作时性能上的差异,而且LinkedList包含的操作比ArrayList多。另一个值得注意的是与Hash相关的类,这种类使用的保存方式会不同于同属的其他类。
List
List接口继承了Collection接口,并在此基础之上添加了一些方法,这些方法支持在List的中间进行元素的插入和移除。
List会以特定的顺序维护元素,由其衍生出了许多有用的类:
接下来会介绍其中两种较为常用的List:
- ArrayList:一种基本的List,擅长随机访问元素,但在List中间进行插入和删除速度较慢。
- LinkedList:具有理想的顺序访问性能,插入和删除的成本较低。除此之外,随机访问性能也更差,但与ArrayList相比有更多的功能。
下述程序将会用到的Pet类笔者只进行了简单实现,大致如下:
因此代码未写入本笔记。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Random;public class ListFeatures {public static void main(String[] args) {List<Pet> pets = new ArrayList<>();Collections.addAll(pets, new Rat(), new Manx(),new Cymric(), new Pug(), new Cymric(), new Pug()); // 插入一些数据System.out.println("1. 创建List:" + pets);System.out.println();Hamster h = new Hamster();pets.add(h);System.out.println("2. 删除元素:" + pets);System.out.println("3. 确定对象是否存在:" + pets.contains(h)); // 按对象搜索pets.remove(h); // 按对象删除System.out.println();Pet tmpp = pets.get(2); // 按下标获取System.out.println("4. 获取元素下标:" + pets.indexOf(tmpp));System.out.println();Pet cymric = new Cymric();System.out.println("5. 试图通过同类对象进行索引:" + pets.indexOf(cymric));System.out.println("6. 试图通过同类对象进行删除:" + pets.remove(cymric));System.out.println("7. 对象能够精确匹配,正常删除:" + pets.remove(tmpp));System.out.println();pets.add(3, new Mouse());System.out.println("8. 借助索引进行插入:" + pets);List<Pet> sub = pets.subList(1, 4);System.out.println("9. 使用区间提取List:" + sub);System.out.println("10. 判断sub是否存在于pets中:" + pets.containsAll(sub));System.out.println();// List<Pet>没有实现Comparator接口。为方便请见,这里进行了现场实现Collections.sort(sub, new Comparator<Pet>() {@Overridepublic int compare(Pet p1, Pet p2) {return p1.toString().compareTo(p2.toString());}});System.out.println("11. 对sub进行原地排序:" + sub);System.out.println("12. containsAll()不关心顺序:" + pets.containsAll(sub));System.out.println();Random random = new Random(12);Collections.shuffle(sub, random);System.out.println("13. 通过随机数使sub进行随机排序:" + sub);System.out.println();List<Pet> copy = new ArrayList<>(pets);sub = Arrays.asList(pets.get(1), pets.get(4));System.out.println("14. 通过asList获取sub:" + sub);copy.retainAll(sub);System.out.println("15. 求交集,保留在copy和sub中都存在的元素:" + copy);System.out.println();copy = new ArrayList<>(pets);System.out.println("16. copy获得了一个pets的副本:" + copy);System.out.println();System.out.println("17. 检测pets是否为空:" + pets.isEmpty());pets.clear();System.out.println("18. pets被clear()清空:" + pets.isEmpty());System.out.println();Object[] o = copy.toArray();System.out.println("19. toArray()可以将任意Collection转换为一个数组:" + o[3]);}
}
程序执行的结果是:
如之前所说的,List可以在创建后添加或移除元素,而且可以自己调整大小。
当需要查找某个元素的索引编号,或按照引用从List中删除某个元素时,都会使用到equal()方法(这个方法是Object的组成部分)。另外,每一个Pet都是一个独立的对象,这就是为什么输出5和输出6无法进行匹配的原因。
应该意识到,List的因为会随着equal()行为的改变而改变。
尽管插入和删除的操作需要较高的成本,但这并不意味着我们要在插入或删除时将一个ArrayList转变为LinkedList。这只意味着我们需要注意这个问题,若ArrayList执行多次插入后程序变慢,我们应该看看List的实现(可以使用分析器)。这种优化较为棘手,若不必担心可以暂时放到一边。
Iterator(迭代器)
不管是哪种集合,都会需要通过某种方式完成元素的存取。比如List中的add()和get()。
但当我们开始思考更加复杂的代码时,会发现这样一个缺点:要使用集合,编写程序时就必须考虑集合的确切类型。如果我一开始针对List编写代码,进行优化时却希望将集合修改为Set,这时就麻烦了。
为此,就出现了迭代器(它同时也是一种设计模式)的概念。迭代器是一个对象,可以在序列中移动,并用来选择序列中的每个对象。并且使用它的程序员不需要知道序列的底层结构。
另外,迭代器是一个轻量级对象,创建成本很低。也因此,迭代器往往会具有一些使用限制。
Java中的Iterator只能向一个方向移动,并且只能实现几个功能:
- iterator():让Collection返回一个Iterator。此时,这个迭代器会准备好返回序列中的第一个元素。
- next():获得序列中的下一个对象。
- hasNext():检测序列是否存在更多对象。
- remove():删除迭代器最近返回的元素。
下列例子中的PetCreator().list()能够返回一个装载了随机Pet对象的ArrayList(笔者是使用了switch语句对这个方法进行了实现)。
import java.util.Iterator;
import java.util.List;public class SimpleIteration {public static void main(String[] args) {// PetCreator().list()会返回一个ArrayList,其中包含随机填充的Pet对象List<Pet> pets = new PetCreator().list(12);Iterator<Pet> it = pets.iterator();while (it.hasNext()) {Pet p = it.next();System.out.print(p.id() + ":" + p + " ");}System.out.println();// 可以使用一种更简单的方式for (Pet p : pets)System.out.print(p.id() + ":" + p + " ");System.out.println();// 也可以使用迭代器来删除元素it = pets.iterator();for (int i = 0; i < 6; i++) {it.next();it.remove();}System.out.println(pets);}
}
程序执行的结果如下:
Iterator的存在使得程序员不再需要操心集合中的元素数量,因为使用hasNext()和next()就能做到不错的处理。
若只需要对List进行遍历,for-in语句显得更加简洁。
这种对集合中的每个对象执行一个操作的想法十分有用,可以在许多地方发现。
上述的例子还无法完全展示Iterator的作用,下面的例子将会创建一个与具体的集合类型无关的display()方法:
注:为了实现这一例子,自制的Pet类需要继承并实现Comparable接口。其中需要被实现的compareTo()方法就是为了进行对象排序而定义的。
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;public class CrossCollectionIteration {public static void display(Iterator<Pet> it) {while (it.hasNext()) {Pet p = it.next();System.out.print(p.id() + ":" + p + " ");}System.out.println();}public static void main(String[] args) {List<Pet> pets = new PetCreator().list(8);LinkedList<Pet> petsLL = new LinkedList<>(pets);HashSet<Pet> petsHS = new HashSet<>(pets);TreeSet<Pet> petsTS = new TreeSet<>(pets);display(pets.iterator());display(petsLL.iterator());display(petsHS.iterator());display(petsTS.iterator());}
}
程序执行的结果如下:
这个例子体现了Iterator真正的作用:能够将序列的遍历操作和序列的底层结构分离。也就是说,迭代器统一了对集合的访问。
Iterable接口描述了“任何可以产生一个迭代器的事物”。
使用这一接口可以将上述例子修改成更加简洁的版本:
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeSet;public class CrossCollectionIteration2 {public static void display(Iterable<Pet> ip) {Iterator<Pet> it = ip.iterator();while (it.hasNext()) {Pet p = it.next();System.out.print(p.id() + ":" + p + " ");}System.out.println();}public static void main(String[] args) {List<Pet> pets = new PetCreator().list(8);LinkedList<Pet> petsLL = new LinkedList<>(pets);HashSet<Pet> petsHS = new HashSet<>(pets);TreeSet<Pet> petsTS = new TreeSet<>(pets);// 可以直接传入数组display(pets);display(petsLL);display(petsHS);display(petsTS);}
}
程序执行的结果与上面的例子无异,不再重复展示。在这里,因为ArrayList已经实现了Iterable接口,所以display()方法在调用时更加方便了。
ListIterator是Iterator的一个更强大的子类型,只有List类才会生成。与Iterator不同,ListIterator可以双向移动。除此之外,它还可以生成相对于迭代器在列表中指向的当前位置的下一个和上一个元素的索引。并且可以使用set()方法替换它所访问过的最后一个元素。
import java.util.List;
import java.util.ListIterator;public class ListIteration {public static void main(String[] args) {List<Pet> pets = new PetCreator().list(8);ListIterator<Pet> it = pets.listIterator();while (it.hasNext())System.out.print(it.next() + ", " + it.nextIndex() + ", " + it.previousIndex() + "; ");System.out.println();// 也可以进行反向的遍历while (it.hasPrevious())System.out.print(it.previous().id() + " ");System.out.println();System.out.println(pets);it = pets.listIterator(3);while (it.hasNext()) { // 从下标为3的位置开始替换对象it.next();it.set(new PetCreator().get());}System.out.println(pets);}
}
程序执行的结果是:
相关文章:

初识Java 10-1 集合
目录 泛型和类型安全的集合 基本概念 添加一组元素 打印集合 List Iterator(迭代器) 本笔记参考自: 《On Java 中文版》 在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着&am…...
Linux- pipe()系统调用
管道 管道(Pipe)是一种用于进程间通信(IPC)的简单而有效的方式。在UNIX和类UNIX操作系统(如Linux)中,管道提供了一种让一个进程将其输出发送给另一个进程的输入的机制。管道通常用于数据流的单…...

数据库常用指令
检查Linux系统是否已经安装了MySQL: sudo service mysql start...
[Studio]Manifest merger failed with multiple errors, see logs 解决方法
记录一个引入库时经常会出错的问题 最近使用一个图片上传库后项目代码报了一个错: Execution failed for task :app:processDebugManifest. > Manifest merger failed with multiple errors, see logs* Try: Run with --info or --debug option to get more lo…...

【数据结构与算法】不就是数据结构
前言 嗨喽小伙伴们你们好呀,好久不见了,我已经好久没更新博文了!之前因为实习没有时间去写博文,现在已经回归校园了。我看了本学期的课程中有数据结构这门课程(这么课程特别重要),因为之前学过一点…...

封装一个高级查询组件
封装一个高级查询组件 背景一,前端相关代码二,后端相关代码三,呈现效果总结 背景 业务有个按照自定义选择组合查询条件,保存下来每次查询的时候使用的需求。查了一下项目里的代码没有现成的组件可以用,于是封装了一个 …...
代码随想录第七章 栈与队列
1、leecode232 用栈实现队列 使用栈模拟队列的行为,仅使用一个栈是不行的,所以需要两个栈,一个是输入栈,一个是输出栈。 #include<iostream> #include<vector> #include<string> #include<stack> #incl…...
SQL Server对象类型(5)——4.5. 同义词(Synonym)
4.5. 同义词(Synonym) 4.5.1. 同义词概念 与Oracle中相同,SQL Server中的同义词是虚的、被定义的模式对象,其本身并不存储任何数据。其用途之一就是为其他类型基础对象提供一个别名;用途之二就是为应用提供一个抽象层,以方便后期应用相关的基础对象的更改和维护。用户可…...

IP风险查询:抵御DDoS攻击和CC攻击的关键一步
随着互联网的普及,网络攻击变得越来越普遍和复杂,对企业和个人的网络安全构成了重大威胁。其中,DDoS(分布式拒绝服务)攻击和CC(网络连接)攻击是两种常见且具有破坏性的攻击类型,它们…...

Tune-A-Video论文阅读
论文链接:Tune-A-Video: One-Shot Tuning of Image Diffusion Models for Text-to-Video Generation 文章目录 摘要引言相关工作文生图扩散模型文本到视频生成模型文本驱动的视频编辑从单个视频生成 方法前提DDPMsLDMs 网络膨胀微调和推理模型微调基于DDIM inversio…...
Dataset和DataLoader用法
Dataset和DataLoader用法 在d2l中有简洁的加载固定数据的方式,如下 d2l.load_data_fashion_mnist() # 源码 Signature: d2l.load_data_fashion_mnist(batch_size, resizeNone) Source: def load_data_fashion_mnist(batch_size, resizeNone):"""…...

【跟小嘉学习区块链】二、Hyperledger Fabric 架构详解
系列文章目录 【跟小嘉学习区块链】一、区块链基础知识与关键技术解析 【跟小嘉学习区块链】一、区块链基础知识与关键技术解析 文章目录 系列文章目录[TOC](文章目录) 前言一、Hyperledger 社区1.1、Hyperledger(面向企业的分布式账本)1.2、Hyperledger社区组织结构 二、Hype…...
springboot下spring方式实现Websocket并设置session时间
概述 springboot实现websocket有4种方式 servlet,spring,netty,stomp 使用下来spring方式是最简单的. springboot版本:3.1.2 jdk:17 当前依赖版本 <dependency><groupId>org.springframework.boot<…...

LeetCode算法二叉树—相同的树
目录 100. 相同的树 - 力扣(LeetCode) 代码: 运行结果: 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是…...

搭建Flink集群、集群HA高可用以及配置历史服务器
Flink集群搭建 Flink集群搭建集群规划下载并解压安装包修改集群配置分发安装目录启动集群访问Web UI Flink集群HA高可用概述集群规划配置flink配置master、workers配置ZK分发安装目录启动HA集群测试 Flink参数配置配置历史服务器概述配置启动、停止历史服务器提交一个Job任务查…...

vscode终端中打不开conda虚拟包管理
今天,想着将之前鸽的Unet网络模型给实现一下,结果发现,在vscode中运行python脚本,显示没有这包,没有那包。但是在其他的ipynb中是有的,感觉很奇怪。我检查了一下python版本,发现不是我深度学习的…...

【音视频】MP4封装格式
基本概念 使用MP4box.js查看MP4内部组成结构 整体结构 数据索引(moov)数据流包(mdat) 各个包的位置,大小,信息,时间戳,编码方式等全在数据索引 数据流包只有纯二进制码流数据 数据…...
环境-使用vagrant快速创建linux虚拟机
1.下载软件 虚拟机 Oracle VM VirtualBox 镜像 Vagrant by HashiCorp (vagrantup.com) 如果下载慢,可以复制下载链接,使用迅雷下载 2.安装 根据提示点击下一步即可,建议安装到空间较大的非系统盘。 打开 window cmd 窗口,…...

10.1网站编写(Tomcat和servlet基础)
一.Tomcat: 1.Tomcat是java写的,运行时需要依赖jre,所以要装jdk. 2.建议配置好环境变量. 3.默认端口号8080(业务端口)可能会被占用,建议改一下(本人改成了9999). 4.另一个默认端口是8005(管理端口). 二Servlet基础(编写一个hello world代码): 整体分为7个步骤,分别是创建…...

10CQRS
本系列包含以下文章: DDD入门DDD概念大白话战略设计代码工程结构请求处理流程聚合根与资源库实体与值对象应用服务与领域服务领域事件CQRS(本文) 案例项目介绍 # 既然DDD是“领域”驱动,那么我们便不能抛开业务而只讲技术&…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...

(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...

Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...

如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
比较数据迁移后MySQL数据库和OceanBase数据仓库中的表
设计一个MySQL数据库和OceanBase数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...