初识Java 10-1 集合

news2024/11/28 6:27:24

目录

泛型和类型安全的集合

基本概念

添加一组元素

打印集合

List

Iterator(迭代器)


本笔记参考自: 《On Java 中文版》


        在进行程序设计时我们会发现,程序总是会根据某些在运行时才能知道的条件来创建新的对象。这意味着,我们必须能够随时随地创建任意数量的对象。

        Java提供了几种方法来持有对象(的引用),其中之一就是数组。数组的优点在于其的高效,但其本身却会受到自身大小固定的制约。实际上,Java.util库提供了一组集合类来解决这一问题,其中包括的基本类ListSetQueueMap也被称为容器类

    Java并没有直接为集合提供关键字支持。

泛型和类型安全的集合

        在Java 5之前,编译器允许向集合中插入不正确的类型。在举例之前需要先对ArrayList类进行一些必要的说明:

        ArrayList是是一种泛型类型,它实现了迭代器(Iterable)、CollectionList等接口。泛型是一种参数化类型的机制,在这种机制下,形参的数据类型也会成为可传输的参数。因为泛型的存在,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只有在运行时才会被检测出来
        }
    }

}

        上述程序的错误不会在编译时被发现,这个报错会出现在运行时:

        AppleOrange唯一的联系在于它们都继承了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的集合类库是用来“持有对象”的。从设计上,可以将这个库分为两种概念,这两种概念分别表示库的两个基本接口:

  1. Collection:一个由单独元素组成的序列,这些元素要符合一条或多条规则。

  2. 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中的ArraysCollection类都包含了一些工具方法,用于向一个Collection中添加一组元素:

  1. Arrays.asList():可以接受一个数组,或一个使用逗号分隔的元素列表,将其转换为一个List对象。
  2. 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:每个槽内持有两个对象,即和与之关联的

        除此之外,上述例子中ArrayListLinkedList的区别不仅在于进行一些操作时性能上的差异,而且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>() {
            @Override
            public 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()方法在调用时更加方便了。

        ListIteratorIterator的一个更强大的子类型,只有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);
    }
}

        程序执行的结果是:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1028561.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3 - 实现动态获取菜单路由和按钮权限控制指令

GitHub Demo 地址 在线预览 前言 关于动态获取路由已在这里给出方案 Vue - vue-admin-template模板项目改造&#xff1a;动态获取菜单路由 这里是在此基础上升级成vue3和ts&#xff0c;数据和网络请求是通过mock实现的 具体代码请看demo!!! 本地权限控制&#xff0c;具体是通过…

关于若依(ruoyi)前端,f12跟踪失效的问题处理

1、根据作者反馈&#xff0c;使用了vite-plugin-vue-setup-extend该插件&#xff1b; 2、参考作者指导&#xff0c;我采用了去掉这个插件的方法&#xff1b; 具体操作&#xff1a; &#xff08;1&#xff09;找到package.json,去掉该插件&#xff1b; &#xff08;2&#xff…

新的小伙伴加入,开始系统更新分享了

近几个月一直有一个好消息未跟大家分享&#xff0c;就是我们有新的小伙伴加入了&#xff0c;帅就不必说了&#xff0c;关键是对电控的理解那可不是一般的强&#xff0c;工程经验丰富&#xff0c;学术能力也是一等一的。我们有幸在一个公司工作&#xff0c;跟着一个企业导师学习…

10个值得关注的学习网站,知乎超30万人收藏,什么资源都可找到!

hi&#xff0c;大家好我是技术苟&#xff0c;每周准时上线为你带来实用黑科技&#xff01;由于公众号改版&#xff0c;现在的公众号消息已经不再按照时间顺序排送了。因此小伙伴们就很容易错过精彩内容。喜欢黑科技的小伙伴&#xff0c;可以将黑科技百科公众号设为标星&#xf…

Webstorm怎么导入插件

Webstorm怎么导入插件&#xff1a; 1.点击“File”&#xff0c;选择“Settings” 2.选择“Plugins” 3.如下图所示继续操作 4.选择想要导入的插件

【C++/Python】Windows用Swig实现C++调用Python(史上最简单详细,80岁看了都会操作)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

自动化测试:yaml结合ddt实现数据驱动!

在pythonunittestseleniumddt的框架中&#xff0c;数据驱动常见有以下几种方式实现&#xff1a; Csv/txtExcelYAML 本文主要给大家介绍测试数据存储在YAML文件中的使用场景。首先先来简单介绍一下YAML。 1. 什么是YAML 一种标记语言类似YAML&#xff0c;它实质上是一种通用…

方案:浅析AI视频分析与视频监控技术的工厂车间智能化监管方案

一、方案背景 工厂生产车间一般是从原材料到成品的流水作业&#xff0c;有大量器械和物料。为保障车间财产安全并提高生产效率&#xff0c;需要进行全面的监管。在生产制造流水线的关键工序中&#xff0c;不仅有作业过程监管需求&#xff0c;同时&#xff0c;也存在生产发生异…

全网最全知识图谱讲解!

什么是知识图谱 知识图谱标准化白皮书定义&#xff1a;知识图谱&#xff08;Knowledge Graph&#xff09;以结构化的形式描述客观世界中概念、实体及其关系&#xff0c;将互联网的信息表达成更接近人类认知世界的形式&#xff0c;提供了一种更好地组织、管理和理解互联网海量信…

Jmeter怎么实现接口关联?

用于接口测试时&#xff0c;后一个接口经常需要用到前一次接口返回的结果&#xff0c;应该如何获取前一次请求的结果值&#xff0c;应用于后一个接口呢&#xff0c;拿一个登录的例子来说明如何获取。 1、打开jmeter&#xff0c;新建一个测试计划&#xff0c;在测试计划里新建一…

分享基于SringBoot足球训练俱乐部系统Python训练打卡系统(源码+调试+lw)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

SMS--短信服务

1 短信服务介绍 短信服务&#xff08;Short Message Service&#xff09;是阿里云为用户提供的一种通信服务的能力。 2 短信服务使用 接下来,我们使用短信验证码功能来演示短信服务的使用。流程如下: 2.1 准备工作 2.1.1 实名认证 https://help.aliyun.com/document_detail…

智能箱式浪涌保护器综合行业解决方案

智能箱式浪涌保护器是一种集成了多种功能的浪涌保护装置&#xff0c;它可以对电力系统、通信系统、计算机系统、工业控制系统等设备提供有效的防雷和过电压保护。本文将详细介绍智能箱式浪涌保护器的作用和原理&#xff0c;以及在不同行业中的应用方案&#xff0c;并参考相关的…

【Hash表】两数之和-力扣 1 题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…

【国产32位mcu】电动车控制芯片CS32F031C8T6的应用

近年来&#xff0c;随着“新国标”的落地&#xff0c;双轮电动车在智能化、强性能、安全性等方面不断演进&#xff0c;带动了新一轮的换车高峰。电动车控制器作为双轮电动车的核心部件&#xff0c;迎来新的增长。 芯海科技32位MCU CS32F031C8T6&#xff0c;作为电动车控制器的…

多因素身份验证MFA功能

随着信息技术的不断进步&#xff0c;网络威胁也随之不断升级和演化。为了保护敏感数据和网络资源&#xff0c;企业和组织需要采取更多的安全措施强化信息安全。多因素身份验证&#xff08;MFA&#xff09;已经成为了现代安全战略的核心组成部分之一。 在这篇文章中&#xff0…

软件工程第一次作业参考答案

题目 名词解释&#xff1a;软件危机、软件、软件工程、软件生命周期、瀑布模型、原型模型、增量模型、喷泉模型、敏捷过程模型。 答案 软件危机&#xff1a;软件危机是指在软件开发过程中所面临的一系列问题和挑战&#xff0c;包括成本超支、进度延误、质量不达标等。 软件…

机器学习之感知机原理及Python实现

机器学习算法感知机(perceptron)。感知机是一种较为简单的二分类模型&#xff0c;但由简至繁&#xff0c;感知机却是神经网络和支持向量机的基础。感知机旨在学习能够将输入数据划分为1/-1的线性分离超平面&#xff0c;所以说整体而言感知机是一种线性模型。因为是线性模型&…

全志H3 Linux编译尝试

全志H3 Linux编译尝试 主要参考&#xff1a;https://blog.csdn.net/qq_40731414/article/details/118684473部分内容介绍来自GPT&#xff0c;但是代码部分都会进行测试 一、简介 Linux编译的作用&#xff1a; 生成可执行的内核映像&#xff1a;编译Linux内核的主要目的是从源代…

使用GPT训练中秋古诗写作讲解

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…