JDK8新特性(一)集合之 Stream 流式操作

1.Stream流由来

       首先我们应该知道:Stream流的出现,主要是用在集合的操作上。在我们日常的工作中,经常需要对集合中的元素进行相关操作。诸如:增加、删除、获取元素、遍历。

        最典型的就是集合遍历了。接下来我们先举个例子来看看 JDK8 Stream流式操作出现之前,我们对集合操作的过程,从中来了解一下 JDK8 之前集合操作数据的弊端。

     Demo:现在有一个List集合,集合中有如下数据:"张无忌"、"周芷若"、"杨逍"、"张强"、"张三丰"、"赵敏"

    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list,"张无忌","周芷若","杨逍","张强","张三丰","赵敏");/*** 需求:*   1.拿到所有姓"张"的名字*   2.拿到长度为3个字的名字*   3.将最终结果进行打印*///JDK8 以前遍历操作集合for (String name : list) {if(name.startsWith("张") && name.length() == 3){System.out.println(name);}}//JDK8 以后使用Stream()流遍历操作集合/**************3.使用 Stream流来操作******************/list.stream().filter(name.startsWith("张") && name->name.length()==3).forEach(name-> System.out.println("111------"+name));}

       使用stream流式操作,直接阅读代码的字面意思即可完美展示无关逻辑方式的语义:①获取流  ② 过滤姓张  ③过滤长度为3  ④遍历打印。我们真正要做的事情内容便能够被更好的体现在代码中。

2.Stream流式思想

  注意:Stream流 和 IO 流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象。

       Stream流式思想类似于工厂车间的"生产流水线",Stream流不是一种数据结构,不会保存数据,而是对数据进行加工处理。Stream 可以看做是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

3.获取Stream流的两种方式 

java.util.stream.Stream<T> 是 JDK8 新加入的流接口。获取一个流非常简单,有以下两种常用的方式:

1.所有的 Collection 集合都可以通过 .stream() 方法来获取流;
2.使用 Stream 接口的 .of() 静态方法,可以获取流。
    public static void main(String[] args) {//方式1:根据Collection获取流//Collection接口中有一个默认的方法:default Stream<E> stream()//1.List获取流List<String> list = new ArrayList<>();Stream<String> stream01 = list.stream();//2.Set获取流Set<String> set = new HashSet<>();Stream<String> stream02 = set.stream();//3.Map获取流//Map 并没有继承自 Collection 接口,所有无法通过该 map.stream()获取流。但是可用通过如下三种方式获取:Map<String,String> map = new HashMap<>();Stream<String> stream03 = map.keySet().stream();Stream<String> stream04 = map.values().stream();List<Map.Entry<String, String>> collect = map.entrySet().stream().collect(Collectors.toList());Stream<Map.Entry<String, String>> stream = map.entrySet().stream();//方式2:Stream中的静态方法of获取流// static<T> Stream<T> of(T... values)// T... values:可变参数,实际原理就是可变数组(传递String数组进去)//1.字符串获取流Stream<String> stream06 = Stream.of("aa", "bb", "cc");//2.数组类型(基本类型除外)String[] strs = {"aa","bb","cc"};Stream<String> stream07 = Stream.of(strs);//3.基本数据类型的数组int[] arr = {1,2,3,4};//看着没报错,但是看到返回值是 int[],这是 Stream流把整个数组看做一个元素来操作,而不是操作数组中的int元素(这样子是不行的!!!)Stream<int[]> stream08 = Stream.of(arr);}

4.Stream流常用方法

      Stream流模型的操作很丰富,这里介绍一些常用的API。这些方法可以被分成两种类型:

  •  终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count() forEach() 方法;
  • 非终结方法:又叫函数拼接方法。值返回值类型仍然是 Stream 类型的方法,支持链式调用(除了终结方法外,其与方法均为非终结方法)

5.Stream流使用注意事项

Stream流只能操作一次;
Stream方法返回的是新的流;
Stream不调用终止方法,中间的操作不会执行。

6.Stream流常用方法 

   提醒以下所有代码部分,能简化部分尽量简化,均使用最简格式!!! 

 1.forEach 

void forEach(Consumer<? super T> action);

        forEach() 方法用来遍历流中的数据,是一个终结方法。该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。示例如下:

public class StreamDemo{public static void main(String[] args){List<String> list = new ArrayList<>();Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");//forEach()遍历//未简写//list.forEach((String str)->{//    System.out.println(str);//});//简写1//list.forEach(str-> System.out.println(str));//最终简写list.forEach(System.out::println);}
}

测试结果: 

2.count 

 long count();

 count() 方法,用来统计集合中的元素个数,是一个终结方法。该方法返回一个 long 值代表元素个数,示例如下:

    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "Mary", "Lucy", "James", "Johson", "Steve");//count()计算集合中元素个数long count = list.stream().count();System.out.println("元素个数为:" + count);}

 

3.filter 

Stream<T> filter(Predicate<? super T> predicate);

filter() 方法,用于过滤数据,返回符合过滤条件的数据,是一个非终结方法。我们可以通过 filter() 方法将一个流转换成另一个子集流。该接口接收一个 Predicate 函数式接口参数(可以是一个 Lambda 或 方法引用) 作为筛选条件。

    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");//filter()过滤,返回以"J"开头的名字list.stream().filter(str->str.startsWith("J")).forEach(System.out::println);//写法二list.stream().filter(str->{if (str.startsWith("J")){return true;}return false;}).forEach(System.out::println);}

  测试结果:

4.limit

Stream<T> limit(long maxSize);

       limit() 方法,用来对 Stream 流中的数据进行截取,只取用前 n 个,是一个非终结方法。参数是一个 long 型,如果集合当前长度大于参数则进行截取,否则不进行操作。因为 limit() 是一个非终结方法,所以必须调用终止方法。示例如下:

    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "Mary", "Lucy", "James", "Johson", "Steve");//limit()截取,截取list集合前三个元素list.stream().limit(3).forEach(System.out::println);}

 5.skip

      如果希望跳过前几个元素,去取后面的元素,则可以使用 skip()方法,获取一个截取之后的新流,它是一个非终结方法。参数是一个 long 型,如果 Stream 流的当前长度大于 n,则跳过前 n 个,否则将会得到一个长度为 0 的空流。因为 limit() 是一个非终结方法,所以必须调用终止方法。示例如下:

Stream<T> skip(long n);
    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list,"Mary","Lucy","James","Johson","Steve");//skip()跳过list集合前2个元素,获取剩下的元素list.stream().skip(2).forEach(System.out::println);}

 备注:使用 skip() 和 limit() 方法,即可实现类似分页的操作了。示例如下:

    //一页10条 分页操作//第一页skip(0).limit(10)//第二页skip(10).limit(10)//第三页skip(20).limit(10)...

6.map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

      map() 方法,可以将流中的元素映射到另一个流中。即:可以将一种类型的流转换为另一种类型的流(区别:map返回的是指定类型(比如int),而flatMap返回的还是一个Stream流),map() 方法是一个非终结方法。该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

       这个方法有三个对于原始类型的变种方法,分别是:mapToInt,mapToLong 和 mapToDouble。这三个方法也比较好理解,比如 mapToInt 就是把原始 Stream 转换成一个新的 Stream,这个新生成的 Stream 中的元素都是 int 类型。之所以会有这样三个变种方法,可以免除自动装箱/拆箱的额外消耗。(参考:本文 15 mapToInt/mapToLong/mapToDouble)

       因为 map() 方法是一个非终结方法,所以必须调用终止方法。通过如下示例,使用 map() 方法,通过方法引用,便将字符串类型转换成了 Integer 类型。示例如下:

    public static void main(String[] args) {List<String> list = new ArrayList<>();Collections.addAll(list, "11", "22", "33", "44", "55");//通过map()方法,可以将String类型的流转换为int类型的流/*list.stream().map((String str)->{return Integer.parseInt(str);}).forEach((Integer num) -> {System.out.println(num);});*///简化://list.stream().map(str->Integer.parseInt(str)).forEach(str->System.out.println(str));//简化后:list.stream().map(Integer::parseInt).forEach(System.out::println);}

 7.flatMap

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

flatMap 的使用,同 map 类似。map只是一维 1对1 的映射,返回的是指定的类型;

      而flatMap返回的则还是一个Stream流,可以对其进行进一步操作。(区别:map返回的是指定类型(比如int),而flatMap返回的还是一个Stream流)

      我的理解为:假如你的集合流中包含子集合(或者需要更深进一步操作),那么使用 flatMap 可以返回该子集合的集合流。示例代码如下所示:

public class Province {private String name;private List<String> city;//get/set 方法
}public class flatMapDemo{public static void main(String[] args) {List<Province> provinceList = new ArrayList<>();List<String> bjCityList = new ArrayList<>();bjCityList.add("海淀");bjCityList.add("朝阳");List<String> shCityList = new ArrayList<>();shCityList.add("黄埔");shCityList.add("闵行");Province bjProvince = new Province();bjProvince.setName("北京");bjProvince.setCity(bjCityList);provinceList.add(bjProvince);Province shProvince = new Province();shProvince.setName("上海");shProvince.setCity(shCityList);provinceList.add(shProvince);//使用map,需要多次forEachprovinceList.stream().map(str->str.getCity()).forEach(cityList -> cityList.forEach(System.out::println));System.out.println("----------");//使用 flatMapprovinceList.stream().flatMap(str->str.getCity().stream()).forEach(System.out::println);}
}

 测试结果:

8.sorted 

sorted() 方法,可以用来对 Stream 流中的数据进行排序。sorted()方法共有以上两种情况:

    //根据元素的自然规律排序Stream<T> sorted();//根据比较器指定的规则排序Stream<T> sorted(Comparator<? super T> comparator);

       因为 sorted() 方法是一个非终结方法,所以必须调用终止方法。

       场景:①sorted() 方法:按照自然规律,默认为升序排序。

                  ②sorted(Comparator comparator)  方法,按照指定的比较器规则排序(以降序为例)。 示例如下:

    public static void main(String[] args){//sorted():根据元素的自然规律排序Stream<Integer> stream01 = Stream.of(66,33,11,55);stream01 .sorted().forEach(System.out::println);System.out.println("   -----  ");//sorted(Comparator<? super T> comparator):根据比较器规则降序排序Stream<Integer> stream02 = Stream.of(66,33,11,55);stream02 .sorted((i1,i2)-> i2-i1).forEach(System.out::println);}

9.distinct 

distinct() 方法,可以用来去除重复数据。因为 distinct() 方法是一个非终结方法,所以必须调用终止方法。

       去除重复数据,此处有几种情况:①基本类型去重   ②String类型去重    ③引用类型去重(对象去重)

       ①②使用 distinct() 方法可以直接去重  ③对象类型需要重写 equals() 和 hasCode() 方法,使用 distinct() 方法才能去重成功。示例如下:

public static void main(String[] args){//基本类型去重Stream<Integer> stream01 = Stream.of(66,33,11,55,33,22,55,66);stream01 .distinct().forEach(System.out::println);//字符串去重Stream<String> stream02 = Stream.of("AA","BB","AA");stream02.distinct().forEach(System.out::println);//自定义对象去重//(Person对象类,有String name,Integer age 两个属性,两参数构造器,get/set()方法,重写了equals(),hashCode(),toString()方法。此处就不附Person实体类了)BiFunction<String,Integer,Person> fn1 = Person::new;Stream<Person> stream14 = Stream.of(fn1.apply("西施", 18), fn1.apply("貂蝉", 20), fn1.apply("王昭君", 22), fn1.apply("杨玉环", 23), fn1.apply("杨玉环", 23));stream14.distinct().forEach(System.out::println);}

 测试结果:

    6633115522----AABB----Person{name='西施', age=18}Person{name='貂蝉', age=20}Person{name='王昭君', age=22}Person{name='杨玉环', age=23}

10.match

//allMatch 全匹配(匹配所有,所有元素都需要满足条件-->返回true)
boolean allMatch(Predicate<? super T> predicate);
//anyMatch 匹配某个元素(只要有一个元素满足条件即可-->返回true)
boolean anyMatch(Predicate<? super T> predicate);
//noneMatch 匹配所有元素(所有元素都不满足指定条件-->返回true)
boolean noneMatch(Predicate<? super T> predicate);

       match() 方法,可以用来判断 Stream 流中的数据是否匹配指定的条件。allMatch()、anyMatch()、noneMatch() 方法都是终结方法,返回值为 bollean。示例如下:

    public static void main(String[] args){Stream<Integer> stream01 = Stream.of(5, 3, 6, 1);boolean allMatch = stream01.allMatch(i -> i > 0);System.out.println("allMatch匹配:"+allMatch);Stream<Integer> stream02 = Stream.of(5, 3, 6, 1);boolean anyMatch = stream02 .anyMatch(i -> i > 5);System.out.println("anyMatch匹配:"+anyMatch);Stream<Integer> stream03 = Stream.of(5, 3, 6, 1);boolean noneMatch = stream03 .noneMatch(i -> i < 3);System.out.println("noneMatch匹配:"+noneMatch);}

11.max / min 

    Optional<T> max(Comparator<? super T> comparator);Optional<T> min(Comparator<? super T> comparator);

       max() 和 min() 方法,用来获取 Stream 流中的最大值和最小值。该接口需要一个 Comparator 函数式接口参数,根据指定排序规则来获取最大值,最小值。

       为了保证数据的准确性,此处排序规则需要是升序排序。因为:max() 方法获取的是排序后的最后一个值,min() 方法获取的是排序后的第一个值。如果使用降序排序后,那么 max() 和 min() 方法就相反了,就有异常了。示例如下:

    public static void main(String[] args){//max()Stream<Integer> stream01 = Stream.of(33, 11, 22, 5);Optional<Integer> max = stream01.max((i1, i2) -> i1 - i2);System.out.println("最大值:"+max.get());//min()Stream<Integer> stream02 = Stream.of(33, 11, 22, 5);Optional<Integer> min = stream02.min((i1, i2) -> i1 - i2);System.out.println("最小值:"+min.get());}

测试结果:

12.reduce 

//1.
Optional<T> reduce(BinaryOperator<T> accumulator);
//2.
T reduce(T identity, BinaryOperator<T> accumulator);
//3.
<U> U reduce(U identity,BiFunction<U, ? super T, U> accumulator,BinaryOperator<U> combiner);

     如果需要将 Sream 流中的所有数据,归纳得到一个数据的情况,可以使用 reduce() 方法。如果需要对 Stream 流中的数据进行求和操作、求最大/最小值等(都是归纳为一个数据的情况),此处就可以用到 reduce() 方法。示例如下

    public static void main(String[] args){//reduce():求和操作Stream<Integer> stream01 = Stream.of(4,3,5,6);Integer sum = stream01.reduce(0,(x,y)-> x + y);System.out.println("求和:"+sum);//reduce():求最大值操作Stream<Integer> stream03 = Stream.of(4,3,5,6);Integer max= stream03.reduce(0,(x,y)-> x > y ? x : y);System.out.println("最大值为:"+max);//reduce():求最小值操作Stream<Integer> stream02 = Stream.of(4,3,5,6);Optional<Integer> max1= stream02.reduce((x, y)-> x < y ? x : y);System.out.println("最小值为:"+max1.get());}

 测试结果: 

 结果分析:(求和分析) 

  1. 求和流程:

  2. 第一次:将默认值赋值给x,取出集合第一个元素赋值给y

  3. 第二步:将上一次返回的结果赋值给x,取出集合第二个元素赋值给y

  4. 第三步:继续执行第二步(如下图所示)

13.map 和 reduce 方法组合使用 

    map() 和 reduce() 方法组合使用,可以解决很多日常工作中遇到的问题。我们就从如下场景了解:

       场景一:现在有一个 Person 类,有两个属性:name 和 age,新建四个 Person类,然后完成如下操作:① 求出所有年龄的总和   ②求出 Person 类中的最大年龄

    public static void main(String[] args){BiFunction<String,Integer,Person> fn2 = Person::new;//1.求出所有年龄的总和(年龄为int值,默认为0,此处可以使用待默认值的reduce()方法)Stream<Person> stream01 = Stream.of(fn2.apply("刘德华", 58), fn2.apply("张学友", 56), fn2.apply("郭富城", 54), fn2.apply("黎明", 52));//基本写法://Integer total = stream01.map(p -> p.getAge()).reduce(0,(x, y) -> x + y);//(方法引用)简化后:Integer total = stream01.map(p -> p.getAge()).reduce(0,Integer::sum);System.out.println("年龄总和为:"+total);//2.找出最大年龄Stream<Person> stream02 = Stream.of(fn2.apply("刘德华", 58), fn2.apply("张学友", 56), fn2.apply("郭富城", 54), fn2.apply("黎明", 52));//基本写法:        //Integer maxAge = stream02.map(p -> p.getAge()).reduce(0, (x, y) -> x > y ? x : y);//(方法引用)简化后:Integer maxAge = stream02.map(p -> p.getAge()).reduce(0, Integer::max);System.out.println("最大年龄为:"+maxAge);}

 测试结果 

    年龄总和为:220最大年龄为:58

 场景二:统计字符串 "a" 出现的次数

    public static void main(String[] args){Stream<String> stream03 = Stream.of("a", "b", "c", "d", "a", "c", "b", "a");//map() 和 reduce() 方法组合使用Integer aTotal = stream03.map(str -> {if (str == "a") {return 1;} else {return 0;}}).reduce(0, Integer::sum);System.out.println("a次数:"+aTotal);}

 14.mapToInt / mapToDouble / mapToLong

//mapToInt()
IntStream mapToInt(ToIntFunction<? super T> mapper);
//mapToLong()
LongStream mapToLong(ToLongFunction<? super T> mapper);
//mapToDouble()
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);

  我们通过 Stream<Integer>  stream = Stream.of(1,2,3,4,5); 这种方式,返回值为 Stream<Integer> 这种包装类的泛型,这种方式虽然用起来没有问题,但是它在效率上还是存在着一定的问题。

       当我们将一对数字转成 Stream 流时,因为泛型的原因,只能使用 Integer 包装类。会先把这些数字包装成 Integer 类

//1.Integer是一个类,占用的内存肯定比 int 大
//2.Stream流在操作时,会存在自动装箱和拆箱操作
Stream<Integer> stream = Stream.of(2,3,5,6,7);//把大于3的打印出来(num在Stream流中是Integer类型,在与3比较时,显然会存在自动拆装箱问题),效率会有影响
stream.filter(num -> num > 3).forEach(System.out::println);

       所以在 JDK8 中,对 Stream 流还新增了一个 mapToInt()方法。该方法可以将流中操作的 Integer 包装类,在 Stream 流中转换成直接来操作 int 类型,效率明显会高一点。

示例如下:

    public static void main(String[] args){//使用 mapToInt()方法IntStream intStream = Stream.of(1, 2, 3, 4, 5, 6).mapToInt((Integer num) -> {return num.intValue();});//(使用方法引用)简化后IntStream intStream1 = Stream.of(1, 2, 3, 4, 5, 6).mapToInt(Integer::intValue);intStream1.filter(n->n>3).forEach(System.out::println);/*** 使用mapToInt(),返回值是一个IntStream类型.我们看一下它们的继承结构图(如下所示):* IntStream 和 Stream<Integer> 类型进行比较。发现他们都继承自 BaseStream。所以它们区别不大* 只不过 IntStream 内部操作的是 int 基本类型的数据,省去自动拆装箱过程。从而可以节省内存开销*/}

 继承结构图:

提示:

        mapToDouble() / mapToLong() 的使用,与 mapToInt()一致,此处不再介绍。

15.concat 

public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {Objects.requireNonNull(a);Objects.requireNonNull(b);@SuppressWarnings("unchecked")Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>((Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());return stream.onClose(Streams.composedClose(a, b));
}

 concat() 方法,可以将两个Stream流合并成一个流进行返回。如果是三个流,则需要两两合并,不能一次性合并三个流。concat() 方法是 Stream 接口的静态方法,我们可以直接使用类名.方法名】调用。

注意:concat() 方法此处接收的是 Stream 类型,不能接收 IntStream 等类型。concat() 是一个静态方法,与 java.lang.String 中的 concat() 方法是不同的。

    public static void main(String[] args){//concat()方法Stream<Integer> aStream = Stream.of(1, 2, 3);Stream<Integer> bStream = Stream.of(4, 5, 6);Stream<Integer> concatStream = Stream.concat(aStream, bStream);concatStream.forEach(System.out::println);}

 测试结果:

16.peek 

介绍:该方法会生成一个包含原 Stream 的所有元素的新 Stream,同时会提供一个消费函数(Consumer实例),新Stream每个元素被消费的时候都会执行给定的消费函数 

存在此方法的目的,主要是为了在您需要的地方支持调试,查看元素流过管道中特定点的情况

主要用于开发过程中调试使用!!!

    public static void main(String[] args){List<String> list = Stream.of("one", "two", "three", "four").filter(e -> e.length() > 3).peek(e -> System.out.println("大于三---" + e)).map(String::toUpperCase).peek(e -> System.out.println("转大写---" + e)).collect(Collectors.toList());}

 17.allMatch / anyMatch / noneMatch 匹配相关

这三个方法,均返回 boolean 类型 

allMatch:是不是Stream中的所有元素都满足给定的匹配条件
anyMatch:Stream中是否存在任何一个元素满足匹配条件
noneMatch:是不是Stream中的所有元素都不满足给定的匹配条件
    public static void main(String[] args) {
// Stream中元素,所有元素长度都>2boolean flag = Stream.of("one", "two", "three", "four").allMatch(str -> str.length() > 2);System.out.println(flag);     // 返回值:true// Stream中元素,是不是存在任何一个元素长度>4boolean flag1 = Stream.of("one", "two", "three", "four").anyMatch(str -> str.length() > 4);System.out.println(flag1);     // 返回值:true// Stream中元素,所有元素长度没有一个元素<1boolean flag2 = Stream.of("one", "two", "three", "four").noneMatch(str -> str.length() < 1);System.out.println(flag2);     // 返回值:true}

 

18.findFirst / findAny 

findFirst:返回Stream中的第一个元素,如果Stream为空,返回空Optional
findAny:返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个
    public static void main(String[] args) {Optional<String> first = Stream.of("one", "two", "three", "four").findFirst();System.out.println(first.get());Optional<String> any = Stream.of("one", "two", "three", "four").findAny();System.out.println(any.get());Optional<String> any1 = Stream.of("one", "two", "three", "four").parallel().findAny();System.out.println(any1.get());}

 

 19.collect

collect() 方法的使用,也有很多内容学习,此处内容过多,不做一一列举。

       如需了解 Stream 流 collect() 方法的使用介绍,你可以看博主下一文章学习了解。请点击如下链接跳转:JDK8新特性(二):Stream流 collect() 方法的详细使用介绍

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MYSQL之索引语法与使用

索引分类 分类 含义 特点 关键字 主键索引 针对表中主键创建的索引 默认自动创建&#xff0c;只能有一个 PRIMARY 唯一索引 …

Gateway+Springsecurity+OAuth2.0+JWT 实现分布式统一认证授权!

目录 1. OAuth2.0授权服务 2. 资源服务 3. Gateway网关 4. 测试 在SpringSecurityOAuth2.0 搭建认证中心和资源服务中心-CSDN博客 ​​​​​​ 基础上整合网关和JWT实现分布式统一认证授权。 大致流程如下&#xff1a; 1、客户端发出请求给网关获取令牌 2、网关收到请求…

Golang 中如何实现 Set

在Go编程中&#xff0c;数据结构的选择对解决问题至关重要。本文将探讨如何在 GO 中实现 set 和 bitset 两种数据结构&#xff0c;以及它们在Go中的应用场景。 Go 的数据结构 Go 内置的数据结构并不多。工作中&#xff0c;我们最常用的两种数据结构分别是 slice 和 map&#…

K8S--安装Nginx

原文网址&#xff1a;K8S--安装Nginx-CSDN博客 简介 本文介绍K8S安装Nginx的方法。 1.创建Nginx目录及配置文件 mkdir -p /work/devops/k8s/app/nginx/{config,html} 在config目录下创建nginx.conf配置文件&#xff0c;内容如下&#xff1a; # events必须要有 events {wo…

jdk的安装和Tomcat的安装

jdk的安装 双击jdk&#xff0c;然后一路下一步 公共JRE可以关闭&#xff0c;没多大用&#xff0c;反而会占用内存 计算机–>属性–>高级系统设置–>环境变量 系统变量–新建 JAVA_HOMEjdk的存放路径 修改path 在path的最后面添加&#xff08;&#xff1b;%JAVA_H…

即插即用篇 | UniRepLKNet:用于音频、视频、点云、时间序列和图像识别的通用感知大卷积神经网络 | DRepConv

大卷积神经网络(ConvNets)近来受到了广泛研究关注,但存在两个未解决且需要进一步研究的关键问题。1)现有大卷积神经网络的架构主要遵循传统ConvNets或变压器的设计原则,而针对大卷积神经网络的架构设计仍未得到解决。2)随着变压器在多个领域的主导地位,有待研究ConvNets…

【漏洞复现】SpringBlade export-user接口SQL注入漏洞

文章目录 前言声明一、SpringBlade系统简介二、漏洞描述三、影响版本四、漏洞复现五、修复建议 前言 SpringBlade 是一个由商业级项目升级优化而来的微服务架构 采用Spring Boot 2.7 、Spring Cloud 2021 等核心技术构建&#xff0c;完全遵循阿里巴巴编码规范。提供基于React和…

PowerShell install 一键部署grafana

grafana 前言 Grafana 是一款开源的数据可视化和监控仪表盘工具。它提供了丰富的数据查询、可视化和报警功能,可用于实时监控、数据分析和故障排除等领域。 通过 Grafana,您可以连接到各种不同的数据源,包括时序数据库(如 Prometheus、InfluxDB)和关系型数据库(如 MySQ…

Halcon基于形状的模板匹配inspect_shape_model

Halcon基于形状的模板匹配 基于形状的匹配&#xff0c;就是使用目标对象的轮廓形状来描述模板。Halcon中有操作助手&#xff0c;可以直观 地进行形状模板匹配的参数选择以及效果测试。如果使用算子编写&#xff0c;步骤如下。 &#xff08;1&#xff09;从参考图像上选择检测的…

c++:string相关的oj题(415. 字符串相加、125. 验证回文串、541. 反转字符串 II、557. 反转字符串中的单词 III)

文章目录 1. 415. 字符串相加题目详情代码1思路1代码2思路2 2. 125. 验证回文串题目详情代码1&#xff08;按照要求修改后放到新string里&#xff09;思路1代码2(利用双指针/索引)思路2 3. 541. 反转字符串 II题目详情代码1思路1 4. 557. 反转字符串中的单词 III题目详情代码1&…

CocoaPods的安装和使用

前言 本篇文章讲述CocoaPods的安装和使用 安装cocoaPods 如果电脑没有安装过cocoaPods&#xff0c;需要先安装&#xff0c;使用下面的命令&#xff1a; sudo gem install cocoapods输入密码后开始安装&#xff0c;需要等待。。。但是我这里报错了。 The last version of d…

nexus清理docker私库

下载nexus-cli客户端&#xff0c;并非必须下载到服务器&#xff0c;理论上只要能访问到nexus就行 wget https://s3.eu-west-2.amazonaws.com/nexus-cli/1.0.0-beta/linux/nexus-cli这个链接下载不了了&#xff0c;末尾有资源下载&#xff0c;里面包含了完整包和脚本&#xff0…

即插即用篇 | YOLOv8 引入 SENetv2 | 多套版本配合使用

卷积神经网络(CNNs)通过提取空间特征并在基于视觉的任务中实现了最先进的准确性,彻底改变了图像分类。所提出的压缩激励网络模块收集输入的通道表示。多层感知机(MLP)从数据中学习全局表示,在大多数用于学习图像提取特征的图像分类模型中起到关键作用。在本文中,我们引入…

npm ,yarn 更换使用国内镜像源,阿里源,清华大学源

在平时开发当中&#xff0c;我们经常会使用 Npm&#xff0c;yarn 来构建 web 项目。但是npm默认的源的服务器是在国外的&#xff0c;如果没有梯子的话。会感觉特别特别慢&#xff0c;所以&#xff0c;使用国内的源是非常有必要的。 Nnpm&#xff0c; yarn 常用命令 常用命令&am…

Appium 环境配置

Appium 是一个开源的、跨平台的测试框架&#xff0c;可以用来测试 Native App、混合应用、移动 Web 应用&#xff08;H5 应用&#xff09;等&#xff0c;也是当下互联网企业实现移动自动化测试的重要工具。Appium 坚持的测试理念&#xff1a; •无需用户对 App 进行任何修改或…

opencv#27模板匹配

图像模板匹配原理 例如给定一张图片&#xff0c;如上图大矩阵所示&#xff0c;然后给定一张模板图像&#xff0c;如上图小矩阵。 我们在大图像中去搜索与小图像中相同的部分或者是最为相似的内容。比如我们在图像中以灰色区域给出一个与模板图像尺寸大小一致的区域&#xff0c;…

软考系分之计算机网络规划设计、综合布线、RAID和网络存储等

文章目录 1、概要2、网络的三层模型3、综合布线系统4、廉价磁盘冗余阵列&#xff08;RAID&#xff09;5、网络存储6、总结 1、概要 本篇重点介绍计算机网络中的网络规划设计、综合布线、RAID和网络存储。 2、网络的三层模型 三层模型分为核心层、汇聚层和接入层&#xff0c;接…

数组A[m+n]中存放了两个线性表(a1,a2,.....am)和(b1,b2.....bn),将数组中的两个线性表的位置互换,要求空间复杂度为1

要求空间复杂度为O(1)&#xff0c;那么不可以借助辅助数组来完成此操作 算法思路&#xff1a;可先将此数组逆置变成bn,......b1,am,....,a1&#xff0c;然后分别逆转两个线性表的数据元素 算法实现 1、定义一个函数&#xff0c;该函数的功能是可以对一个数组的任意连续的部分进…

UDP和TCP代理协议有什么区别?哪个更好

在互联网的世界里&#xff0c;数据传输的方式有很多种&#xff0c;其中 UDP 和 TCP 是两种常见的传输协议。而代理协议则是为了在网络中传输数据时提供安全、稳定和高效的传输环境。那么&#xff0c;UDP 和 TCP 代理协议有什么区别呢&#xff1f;哪个更好呢&#xff1f;接下来&…

内网穿透的应用-使用Docker搭建一个Wiki.Js知识库系统并实现分享他人远程创作

文章目录 1. 安装Docker2. 获取Wiki.js镜像3. 本地服务器打开Wiki.js并添加知识库内容4. 实现公网访问Wiki.js5. 固定Wiki.js公网地址 不管是在企业中还是在自己的个人知识整理上&#xff0c;我们都需要通过某种方式来有条理的组织相应的知识架构&#xff0c;那么一个好的知识整…