展开菜单
首页 精品内容 本月促销 装机必备 Windows PDF教程 macOS软件 IOS软件 Android AI 专题
全部分类

当前位置:

首页 > 编程开发 > Java8 Stream流式编程的使用方法

Java8 Stream流式编程的使用方法

一、Stream中间操作Stream的中间操作是指在流链当中,可以对数据进行处理操作,包括filter过滤、map映射转换、flatMap合并、distinct去重、sorted排序等操作。这些操作都会返回一个新的Stream流对象,可以通过链式调用多个中间操作进行复杂的数据处理。需要注意的是,中间操作需要具有终止操作才会触发。下面按类别讲解Stream常见的中间操作。1.1、filter:过滤出符合条件的元素filter()方法常用于实现数据过滤,即可以对集合、数组等数据源筛选出符合指定条件的元素,并返

一、Stream中间操作

Stream的中间操作是指在流链当中,可以对数据进行处理操作,包括filter过滤、map映射转换、flatMap合并、distinct去重、sorted排序等操作。这些操作都会返回一个新的Stream流对象,可以通过链式调用多个中间操作进行复杂的数据处理。需要注意的是,中间操作需要具有终止操作才会触发。

下面按类别讲解Stream常见的中间操作。

1.1、filter:过滤出符合条件的元素

filter()方法常用于实现数据过滤,即可以对集合、数组等数据源筛选出符合指定条件的元素,并返回一个新的流。

假设有一个黑名单手机号列表,需要筛选出其中所有开头为“133”的元素,那么可以通过filter()实现——

//将数组转换为一个字符串列表
List numbers = Arrays.asList("13378520000","13278520000","13178520000","13358520000");
//通过stream()方法创建一个流,接着使用filter()方法过滤出前缀为“133”的元素,最终通过collect() 方法将结果收集到一个新列表中
List filterdNumbers = numbers.stream().filter(s -> s.startsWith("133")).collect(Collectors.toList());
System.out.println(filterdNumbers);


//打印结果:[13378520000, 13358520000]

1.2、map:映射转换元素

map()方法用于对流中的每个元素进行映射操作,将其转换为另一个元素或者提取其中的信息,并返回一个新的流。

根据以下两个案例分别学习map()将元素转换为另一个元素以及提取元素其中的信息——

1.2.1、转换元素

假设有一个手机号字符列表,需要根据前7位来确定手机号归属地,那么就需要获取所有手机号前7位子字符串,可以使用map()方法实现:

List numbers = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
//通过stream()方法创建一个流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中
List filterdNumbers = numbers.stream().map(s -> s.substring(0,7)).collect(Collectors.toList());
System.out.println(filterdNumbers);


//打印结果:[1337852, 1327852, 1317852, 1355852]

1.2.2、提取元素信息

假设有一个用户对象列表,我们需要提取其中每个对象的手机号,可以使用map()方法实现:

List peopleList = Arrays.asList(
        new People("王二","13378520000"),
        new People("李二","13278520000"),
        new People("张四","13178520000")
);
//通过stream()方法创建一个流,使用map()方法提取每个用户的手机号,最后使用collect()方法将结果收集到一个新列表中
List tel = peopleList.stream().map(People::getTel).collect(Collectors.toList());
System.out.println(tel);


//打印结果:[13378520000, 13278520000, 13178520000]

1.3、flatMap:将多个流合并为一个流

flatMap()方法可以实现多对多的映射,或者将多个列表合并成一个列表操作。

1.3.1、实现多对多的映射

假设有两组余额列表A和B,需要将A组每个元素都与B组所有元素依次进行相加,可以使用flatMap实现该多对多的映射——

List listA = Arrays.asList(1, 2, 3);
List listB = Arrays.asList(4, 5, 6);
List list = listA.stream().flatMap(a -> listB.stream().map(b -> a +b)).collect(Collectors.toList());
System.out.println(list);


//打印结果:  [5, 6, 7, 6, 7, 8, 7, 8, 9]	

1.3.2、将多个列表合并成一个列表

假设有一个包含多个手机号字符串列表的列表,现在需要合并所有手机号字符串成为一个列表,可以使用flatMap()方法实现:

List> listOfLists = Arrays.asList(
        Arrays.asList("13378520000", "13278520000"),
        Arrays.asList("13178520000", "13558520000"),
        Arrays.asList("15138510000", "15228310000")
);
List flatMapList = listOfLists.stream().flatMap(Collection::stream).collect(Collectors.toList());
System.out.println(flatMapList);


//打印结果:[13378520000, 13278520000, 13178520000, 13558520000, 15138510000, 15228310000]

1.4、distinct:去除重复的元素

distinct()方法可以用来去除流中的重复元素,生成无重复的列表。

假设有一个包含重复手机号字符串的列表,可以使用distinct()去重操作——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");
List disNumbers = numbers.stream().distinct().collect(Collectors.toList());
System.out.println(disNumbers);		


//打印结果:[13378520000, 15138510000, 13178520000]		

注意一点的是,distinct用于针对流作去重操作时,需要确定流中元素实现了equals()和hashCode()方法,因为这两个方法是判断两个对象是否相等的标准。

1.5、sorted:排序元素

sorted()方法用于对流中的元素进行排序。

假设需要对一组People对象按照年龄排序,下面分别按照升序排序和降序排序——

1.5.1、升序排序

默认情况下,是升序排序——

List peopleList = Arrays.asList(
        new People("王二",20),
        new People("李二",30),
        new People("张四",31)
);
List newpeopleList=peopleList.stream().sorted(Comparator.comparing(People::getAge)).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);

打印结果:

People{name='王二', age=20}
People{name='李二', age=30}
People{name='张四', age=31}

1.5.2、降序排序

通过reversed()方法进行逆序排序,也就是将升序排序进行倒序排序——

List peopleList = Arrays.asList(
        new People("王二",20),
        new People("李二",30),
        new People("张四",31)
);
List newpeopleList = peopleList.stream().sorted(Comparator.comparing(People::getAge).reversed()).collect(Collectors.toList());
//打印结果
newpeopleList.stream().forEach(System.out::println);

打印结果:

People{name='张四', age=31}
People{name='李二', age=30}
People{name='王二', age=20}

1.6、peek:查看每个元素的信息,但不修改流中元素的状态

peek()方法用于查看流中的元素而不会修改流中元素的状态,可以在流中的任何阶段使用,不会影响到流的操作,也不会终止流的操作。

List telList = Arrays.asList("13378520000","13278520000","13178520000","13558520000");
telList.stream().peek(t -> System.out.println(t))
        .map(t -> t.substring(0,3))
        .peek(t -> System.out.println(t))
        .collect(Collectors.toList());

打印结果:

            13378520000
            133
            13278520000
            132

peek()方法和forEach很类似,都是可以用于遍历流中的元素,但是,两者之间存在较大的区别。主要一点是,forEach在流中是一个终止操作,一旦调用它,就意味着Stream流已经被处理完成,不能再进行任何操作,例如,无法在forEach之后针对流进行map、filter等操作,但peek方法可以,以上的案例可以看出,在第一次调用peek打印一个元素后,该元素还可以接着进行map操作,进行字符串的前三位截取。

这是peek()方法和forEach最大的区别。

1.7、limit 和 skip:截取流中的部分元素

limit()和skip()都是用于截取Stream流中部分元素的方法,两者区别在于,limit()返回一个包含前n个元素的新流,skip()则返回一个丢弃前n个元素后剩余元素组成的新流。

int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
System.out.print("取数组前5个元素:");
Arrays.stream(arr).limit(5).forEach(n -> System.out.print(n + " ")); // 输出结果为:1 2 3 4 5


System.out.print("跳过前3个元素,取剩余数组元素:");
Arrays.stream(arr).skip(3).forEach(n -> System.out.print(n + " ")); // 输出结果为:4 5 6 7 8 9 10

二、Stream终止操作

Stream的终止操作是指执行Stream流链中最后一个步骤,到这一步就会结束整个流处理。在Java8中,Stream终止操作包括forEach、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst和findAny等。这些终止操作都有返回值。需要注意一点是,如果没有执行终止操作的话,Stream流是不会触发执行的,例如,一个没有终止操作的peek()方法代码是不会执行进而打印——

list.stream().peek(t -> System.out.println("ddd"))

当加上终止操作话,例如加上collect,就会打印出“ddd”——

list.stream().peek(t -> System.out.println("ddd")).collect(Collectors.toList());

下面按类别分别讲解各个终止操作的使用。

2.1、forEach:遍历流中的每个元素

该forEach前面已经提到,这里不做过多介绍。

2.2、count:统计流中元素的数量

count可以统计流中元素的数量并返回结果。

假设有一个包含多个手机号字符串的列表,需要统计去重后的手机号数量,就可以使用count方法——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");
long count = numbers.stream()
        .distinct()//去重
        .count();//统计去重后的手机号
System.out.println(count);


//打印结果:3

2.3、reduce:将流中的所有元素归约成一个结果

reduce()可以将流中的所有元素根据指定规则归约成一个结果,并将该结果返回。

常用语法格式如下:

Optional result = stream.reduce(BinaryOperator accumulator);

可见,reduce方法会返回一个Optional类型的值,表示归约后的结果,需要通过get()方法获取Optional里的值。

假设有一个包含多个手机号字符串的List列表,需要在去重之后,再将列表所有字符串拼按照逗号间隔接成一个字符串返回,那么就可以通过reduce来实现——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15138510000");
Optional result = numbers.stream()
        .distinct() //去重
        .reduce((a ,b) -> a+","+b);//指定规则为,相临两个字符通过逗号“,”间隔
System.out.println(result.get());

//打印结果:13378520000,15138510000,13178520000

2.4、collect:将流中的元素收集到一个容器中,并返回该容器

collect的作用是将流中的元素收集到一个新的容器中,返回该容器。打个比喻,它就像一个采摘水果的工人,负责将水果一个个采摘下来,然后放进一个篮子里,最后将篮子交给你。我在前面的案例当中,基本都有用到collect,例如前面2.1的filter过滤用法中的List filterdNumbers = numbers.stream().filter(s -> s.startsWith("133")).collect(Collectors.toList()),就是将过滤出前缀为“133”的字符串,将这些过滤处理后的元素交给collect这个终止操作。这时collect就像采摘水果的员工,把采摘为前缀“133”的“水果”通过toList()方法收集到一个新的List容器当中,然后交给你。最后你就可以得到一个只装着前缀为“133”的元素集合。

在Java8的collect方法中,除里toList()之外,还提供了例如toSet,toMap等方法满足不同的场景,根据名字就可以知道,toSet()返回的是一个Set集合,toMap()返回的是一个Map集合。

2.5、min 和 max:找出流中的最小值和最大值

min和max用来查找流中的最小值和最大值。

假设需要在查找出用户列表中年龄最小的用户,可以按照以下代码实现——

List peopleList = Arrays.asList(
        new People("王二",20),
        new People("李二",30),
        new People("张四",31)
);
//查找年龄最小的用户,若没有则返回一个null
People people = peopleList.stream().min(Comparator.comparing(People::getAge)).orElse(null);
System.out.println(people);

//打印结果:People{name='王二', age=20}

max的用法类似,这里不做额外说明。

2.6、anyMatch、allMatch 和 noneMatch:判断流中是否存在满足指定条件的元素

2.6.1、anyMatch

anyMatch用于判断,如果流中至少有一个元素满足给定条件,那么返回true,反之返回false,即 true||false为true这类的判断。

假设在一个手机号字符串的List列表当中,判断是否包含前缀为“153”的手机号,就可以使用anyMatch——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000");
boolean hasNum = numbers.stream().anyMatch(n -> n.startsWith("153"));
System.out.println(hasNum);

//打印结果:true

2.6.2、allMatch

allMatch用于判断,流中的所有元素是否都满足给定条件,满足返回true,反之false,即true&&false为false这类判断。

假设在一个手机号字符串的List列表当中,判断手机号是否都满足前缀为“153”的手机号,就可以用allMatch——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "15338510000");
boolean hasNum = numbers.stream().allMatch(n -> n.startsWith("153"));
System.out.println(hasNum);

//打印结果:false

2.6.3、noneMatch

noneMatch用于判断,如果流中没有任何元素满足给定的条件,返回true,如果流中有任意一个条件满足给定条件,返回false,类似!true为false的判断。

假设在一个手机号字符串的List列表当中,判断手机号是否都不满足前缀为“153”的手机号,就可以用noneMatch——

List numbers = Arrays.asList("13378520000", "15138510000","13178520000", "1238510000");
//numbers里没有前缀为“153”的手机号
boolean hasNum = numbers.stream().noneMatch(n -> n.startsWith("153"));
System.out.println(hasNum);


//打印结果:true

这三个方法其实存在一定互相替代性,例如在3.6.1中,满足!anyMatch表示所有手机号都不为“153”前缀,才得到true,这不就是noneMatch,主要看在项目当中如何灵活应用。

2.7、findFirst 和 findAny:返回流中第一个或任意一个元素

2.7.1、findFirst

findFirst用于返回流中第一个元素,如果流为空话,则返回一个空的Optional对象——

假设需要对一批同手机号的黑名单用户按照时间戳降序排序,然后取出第一个即时间戳为最早的用户,就可以使用findFirst——

List peopleList = Arrays.asList(
        new People("王二","13178520000","20210409"),
        new People("李二","13178520000","20230401"),
        new People("张四","13178520000","20220509"),
        new People("赵六","13178520000","20220109")
);
/**
 * 先按照时间升序排序,排序后的结果如下:
 *   People{name='王二', tel='13178520000', time='20210409'}
 *   People{name='赵六', tel='13178520000', time='20220109'}
 *   People{name='张四', tel='13178520000', time='20220509'}
 *   People{name='李二', tel='13178520000', time='20230401'}
 *
 *排序后,People{name='王二', tel='13178520000', time='20210409'}成了流中的第一个元素
 */
People people = peopleList.stream().sorted(Comparator.comparing(People::getTime)).findFirst().orElse(null);
System.out.println(people);

//打印结果:People{name='王二', tel='13178520000', time='20210409'}

2.7.2、findAny

findAny返回流中的任意一个元素,如果流为空,则通过Optional对象返回一个null。

假设有一个已经存在的黑名单手机号列表blackList,现在有一批新的手机号列表phoneNumber,需要基于blackList列表过滤出phoneNumber存在的黑名单手机号,最后从过滤出来的黑名单手机号当中挑选出来出来任意一个,即可以通过findAny实现——

//blackList是已经存在的黑名单列表
List blackList = Arrays.asList("13378520000", "15138510000");
//新来的手机号列表
List phoneNumber = Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000");
String blackPhone = phoneNumber.stream()
        //过滤出phoneNumber有包含在blackList的手机号,这类手机号即为黑名单手机号。
        .filter(phone -> blackList.contains(phone))
        //获取过滤确定为黑名单手机号的任意一个
        .findAny()
        //如果没有则返回一个null
        .orElse(null);
System.out.println(blackPhone);

//打印结果:13378520000

三、并行流

前面的案例主要都是以顺序流来讲解,接下来,就是讲解Stream的并行流。在大数据量处理场景下,使用并行流可以提高某些操作效率,但同样存在一些需要考虑的问题,并非所有情况下都可以使用。

3.1、什么是并行流:并行流的概念和原理

并行流是指通过将数据按照一定的方式划分成多个片段分别在多个处理器上并行执行,这就意味着,可能处理完成的数据顺序与原先排序好的数据情况是不一致的。主要是用在比较大的数据量处理情况,若数据量太少,效率并不比顺序流要高,因为底层其实就使用到了多线程的技术。

并行流的流程原理如下:

1、输入数据:并行流的初始数据一般是集合或者数组,例如Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000");

2、划分数据:将初始数据平均分成若干个子集,每个子集可以在不同的线程中独立进行处理,这个过程通常叫“分支”(Forking),默认情况下,Java8并行流使用到了ForkJoinPool框架,会将Arrays.asList("13378520000", "13178520000", "1238510000","15138510000","13299920000")划分成更小的颗粒进行处理,可能会将该数组划分成以下三个子集:

[13378520000, 13178520000]    [1238510000, 13338510000]    [13299920000]

3、处理数据:针对划分好的子集并行进行相同的操作,例如包括过滤(filter)、映射(map)、去重(distinct)等,这个过程通常叫“计算”(Computing),例如需要过滤为前缀包括“133”的字符集合,那么,各个子集,就会处理得到以下结果:

[13378520000]    [13338510000]    []

4、合并结果:将所有子集处理完成的结果进行汇总,得到最终结果。这个过程通常叫“合并”(Merging),结果就会合并如下:

[13378520000,13338510000]  

5、返回结果:返回最终结果。

通俗而言,就是顺序流中,只有一个工人在摘水果,并行流中,是多个工人同时在摘水果。

3.2、创建并行流:通过 parallel() 方法将串行流转换为并行流

可以通过parallel()方法将顺序流转换为并行流,操作很简单,只需要在顺序流上调用parallel()即可。

List numbers = Arrays.asList("13378360000","13278240000","13178590000","13558120000");
//通过stream().parallel()方法创建一个并行流,使用map()方法将每个字符串转换为截取前7位的字符,最后使用collect()方法将结果收集到一个新列表中
List filNums = numbers.stream().parallel().map(s -> s.substring(0,7)).collect(Collectors.toList());
System.out.println(filNums);


//打印结果:[1337836, 1327824, 1317859, 1355812]

3.3、并行流的注意事项:并行流可能引发的线程安全,以及如何避免这些问题

在使用并发流的过程中,可能会引发以下线程安全问题:并行流中的每个子集都在不同线程运行,可能会导致对共享状态的竞争和冲突。

避免线程问题的方法如下:避免修改共享状态,即在处理集合过程当中,避免被其他线程修改集合数据,可以使用锁来保证线程安全。

使用无状态操作:在并行流处理过程尽量使用无状态操作,例如filter、map之类的,可以尽量避免线程安全和同步问题。

四、Optional

4.1、什么是 Optional:Optional 类型的作用和使用场景

在实际开发当中,Optional类型通常用于返回可能为空的方法、避免null值的传递和简化复杂的判断逻辑等场景。调用Optional对象的方法,需要通过isPresent()方法判断值是否存在,如果存在则可以通过get()方法获取其值,如果不存在则可以通过orElse()方法提供默认值,或者抛出自定义异常处理。

4.2、如何使用 Optional:如何使用 Optional 类型

使用Optional类型主要目的是在数据可能为空的情况下,提供一种更安全、更优雅的处理方式。

以下是使用Optional类型的常用方法:

4.2.1、ofNullable()和isPresent()方法

将一个可能为null的对象包装成Optional类型的对象,然后根据isPresent方法判断对象是否包含空值——

String str = null;
Optional optStr = Optional.ofNullable(str);
if (optStr.isPresent()){
    System.out.println("Optional对象不为空");
}else {
    System.out.println("Optional对象为空");
}

//打印结果:Optional对象为空

4.2.2、get()方法

获取Optional对象中的值,如果对象为空则抛出NoSuchElementException异常——

String str = null;
Optional optStr = Optional.ofNullable(str);
if (optStr.isPresent()){
    System.out.println("Optional对象不为空");
}else {
    System.out.println("Optional对象为空");
    optStr.get();
}

控制台打印结果:

Exception in thread "main" java.util.NoSuchElementException: No value present
    at java.util.Optional.get(Optional.java:135)
    at com.zhu.fte.biz.test.StreamTest.main(StreamTest.java:144)
Optional对象为空

4.2.4、orElse()方法

获取Optional对象中的值,如果对象为空则返回指定的默认值——

String str = null;
Optional optStr = Optional.ofNullable(str);
if (optStr.isPresent()){
    System.out.println("Optional对象不为空");
}else {
    System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));
}


//打印结果:Optional对象为空,返回默认值:null

当然,如果不为空的话,则能正常获取对象中的值——

String str = "测试";
Optional optStr = Optional.ofNullable(str);
if (optStr.isPresent()){
    System.out.println("Optional对象不为空,返回值:" + optStr.orElse("null"));
}else {
    System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));
}

//打印结果:Optional对象不为空,返回值:测试

那么,问题来了,它是否能判断“ ”这类空格的字符串呢,我实验了一下,

String str = "     ";
Optional optStr = Optional.ofNullable(str);
if (optStr.isPresent()){
    System.out.println("Optional对象不为空,返回值:" + optStr.orElse("null"));
}else {
    System.out.println("Optional对象为空,返回默认值:" + optStr.orElse("null"));
}


//打印结果:Optional对象不为空,返回值:

可见,这类空字符串,在orElse判断当中,跟StringUtils.isEmpty()类似,都是把它当成非空字符串,但是StringUtils.isBlank()则判断为空字符串。

4.2.5、orElseGet()方法

orElseGet()和orElse()类似,都可以提供一个默认值。两者区别在于,orElse方法在每次调用时都会创建默认值,而orElseGet只在需要时才会创建默认值。

4.3、Optional 和 null 的区别: Optional 类型与 null 值的异同

两者都可以表示缺失值的情况,两者主要区别为:Optional类型是一种包装器对象,可以将一个可能为空的对象包装成一个Optional对象。这个对象可以通过调用ofNullable()、of()或其他方法来创建。而null值则只是一个空引用,没有任何实际的值。

Optional类型还可以避免出现NullPointerException异常,具体代码案例如下:

String str = null;
//错误示范:直接调用str.length()方法会触发NullPointerException
//int length = str.length()

//通过Optional类型避免NullPointerException
Optional optionalStr = Optional.ofNullable(str);
if (optionalStr.isPresent()){//判断Optional对象是否都包含非空值
    int length = optionalStr.get().length();
    System.out.println("字符串长度为:" + length);
}else {
    System.out.println("字符串为空!");
}

//使用map()方法对Optional对象进行转换时,确保返回对结果不为null
Optional optionalLength = optionalStr.map(s -> s.length());
System.out.println("字符串长度为:" + optionalLength.orElse(-1)); // 使用orElse()方法提供默认值

五、扩展流处理

除里以上常用的流处理之外,Java8还新增了一些专门用来处理基本类型的流,例如IntStream、LongStream、DoubleStream等,其对应的Api接口基本与前面案例相似,读者可以自行研究。

最后,需要注意一点是,在流处理过程当中,尽量使用原始类型数据,避免装箱操作,因为装箱过程会有性能开销、内存占用等问题,例如,当原始数据int类型被装箱成Integer包装类型时,这个过程会涉及到对象的创建、初始化、垃圾回收等过程,需要额外的性能开销。

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
作者最新文章
编程开发 Java
相关文章 更多
精品专题 更多
装机必备

正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

Windows

正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

PDF教程

正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

macOS软件

正软商城macOS软件专区,精选适用于Mac电脑的办公、设计、影音、效率、开发和系统工具,提供软件功能介绍、macOS兼容版本、正版授权及购买下载服务。

IOS软件

正软商城iOS软件专区,精选适用于iPhone和iPad的办公、学习、影音、设计、效率及AI应用,提供功能介绍、适用设备、系统要求和正版获取方式等信息。

AI

正软商城AI软件专区,汇集AI写作、AI绘画、AI视频、AI办公、AI编程、AI翻译、智能客服和数据分析等人工智能工具,提供功能介绍、适用平台、收费方式及正版购买信息。

Mac软件 更多
WPS Office macOS版
WPS Office macOS版

WPS Office是一款面向macOS用户的综合办公套件,将文字处理、电子表格、演示文稿、PDF工具、模板与云协作整合在同一应用中。它兼容常见Microsoft Office格式,支持Intel及Apple芯片Mac,基础功能可免费使用,

Shapr3D macOS版
Shapr3D macOS版

Shapr3D是一款面向工业设计、机械工程、建筑概念和三维打印工作流的CAD软件。Mac版采用Parasolid建模内核,支持草图约束、实体建模、工程图、可视化渲染及常见CAD格式交换,并可通过账户在多台设备之间同步项目。

REAPER macOS版
REAPER macOS版

REAPER是Cockos开发的数字音频工作站,提供多轨音频与MIDI录制、剪辑、处理、混音和母带制作工具。Mac版兼容Intel与Apple芯片,支持AU、VST、VST3、CLAP等插件格式,并提供高度可定制的工作流程。

Ableton Live macOS版
Ableton Live macOS版

Ableton Live 是面向音乐制作人与现场表演者的数字音频工作站,提供编曲视图、独具特色的现场视图、音频录制、MIDI创作、实时变速、乐器及效果器。Mac版原生支持Apple芯片,并可连接音频接口、MIDI控制器和第三方插件。

Adobe After Effects macOS版
Adobe After Effects macOS版

Adobe After Effects 是面向动态图形、影视特效与视频合成的专业创作软件。它提供关键帧动画、遮罩抠像、运动跟踪、三维空间、文字动画和表达式等工具,并可与 Premiere Pro、Photoshop、Illustrator

Adobe Audition macOS版
Adobe Audition macOS版

Adobe Audition 是面向播客、影视后期、音乐制作及内容创作者的专业音频工作站,提供波形精修、多轨录制与混音、频谱编辑、降噪修复、响度匹配和效果处理工具,并可与 Adobe Premiere 协同完成视频声音制作。

Adobe Bridge macOS版
Adobe Bridge macOS版

Adobe Bridge 是面向摄影师、设计师及内容团队的免费数字资产管理工具,可集中预览照片、视频和 Adobe 项目文件,并利用评级、标签、关键词、元数据、筛选与收藏集完成分类检索。它还支持批量重命名、格式导出、相机导入及 Camera

Adobe Illustrator macOS版
Adobe Illustrator macOS版

Adobe Illustrator 是 Adobe 推出的专业矢量图形设计软件,适合在 Mac 上制作标志、图标、插画、包装、信息图及印刷版面。它以路径和锚点构建可无损缩放的图形,并提供钢笔、形状、文字、图像描摹、渐变、画板及生成式功能。

Adobe InDesign macOS版
Adobe InDesign macOS版

Adobe InDesign 是面向印刷品与数字出版物的专业版面设计软件,可在 Mac 上制作书籍、杂志、宣传册、海报、报告及交互式文档。它提供精细的文字样式、网格、母版、长文档管理和印前输出能力。

Adobe Lightroom Classic macOS版
Adobe Lightroom Classic macOS版

Adobe Lightroom Classic 是面向桌面摄影工作流的照片管理与后期处理软件,可将原始照片保存在本地硬盘,通过目录、关键词、评分和收藏夹高效整理图库,并提供 RAW 冲印、蒙版、镜头校正、批量同步及多格式导出等功能。

Adobe Media Encoder macOS版
Adobe Media Encoder macOS版

Adobe Media Encoder 是 Adobe 推出的专业音视频编码与转码工具,可通过队列、预设和监视文件夹批量完成格式转换、代理文件创建及多平台成片输出,并与 Premiere Pro、After Effects 等软件紧密协作。

Adobe Photoshop macOS版
Adobe Photoshop macOS版

Adobe Photoshop 是面向摄影师、设计师和内容创作者的专业图像处理软件,提供图层、蒙版、选区、修复、调色、文字排版、智能对象及生成式编辑等能力。Mac 版支持 Intel 与 Apple Silicon 处理器。

WINDOWS 更多
Adobe Photoshop Windows版
Adobe Photoshop Windows版

Adobe Photoshop 是面向摄影师、设计师和内容创作者的专业图像处理工具,可在 Windows 上完成照片修饰、选区抠图、图层合成、文字排版、绘画及生成式编辑。软件采用订阅授权,由 Creative Cloud 管理安装、更新与许

Adobe InDesign Windows版
Adobe InDesign Windows版

Adobe InDesign 是面向印刷与数字出版的专业页面排版工具,可制作书籍、杂志、宣传册、海报、交互式 PDF 和电子书。它提供精细的文字样式、多页面管理、印前检查及多格式输出能力,并能与 Photoshop、Illustrator

Adobe Illustrator Windows版
Adobe Illustrator Windows版

Adobe Illustrator 是用于绘制矢量图形、插画、图标、徽标和版式的专业设计工具。它以路径和锚点为核心,作品放大后仍能保持清晰,并提供画板、文字、渐变、图像描摹及精确变换等功能,适合品牌视觉、印刷物料、数字界面和商业插画创作。

Adobe Bridge Windows版
Adobe Bridge Windows版

Adobe Bridge 是 Adobe 推出的创意资产管理工具,可集中预览、分类和查找照片、视频、PDF及多种设计文件,并支持元数据、关键词、评级、批量重命名和格式导出。它可与 Photoshop、Illustrator 等应用衔接,

Affinity Photo Windows版
Affinity Photo Windows版

Affinity Photo 是一款面向摄影师、修图师和视觉设计人员的桌面图像编辑工具,提供 RAW 显影、多图层合成、蒙版、实时滤镜、批处理和色彩管理等功能。原独立产品已停止销售,其照片编辑能力已延续至免费的新 Affinity 应用;

Adobe Lightroom Classic Windows版
Adobe Lightroom Classic Windows版

Adobe Lightroom Classic 是面向 Windows 桌面摄影工作流的照片管理与无损编辑工具,可通过目录集中整理大型图库,并完成 RAW 显影、局部蒙版、批量调整、打印和多格式导出。它强调本地文件管理与精细控制,

Figma Windows版
Figma Windows版

Figma 是面向产品设计与开发团队的云端协作平台,可在 Windows 桌面客户端中完成界面绘制、组件管理、交互原型、评论评审和开发交付。文件保存在云端,多名成员能够同步编辑并共享设计系统;官方同时提供免费 Starter 方案及按席位计

Affinity Designer Windows版
Affinity Designer Windows版

Affinity Designer 是面向矢量图形、数字插画和界面设计的专业创作工具,可在矢量、像素与导出工作区之间快速切换。它提供曲线编辑、形状构建、画板、排版、图层和专业色彩管理,并支持常见设计文件的导入及多种印刷、网页格式输出。

Capture One Pro Windows版
Capture One Pro Windows版

Capture One Pro 是面向摄影师和影像团队的专业桌面照片处理工具,提供相机定制色彩配置、RAW转换、图层与蒙版、联机拍摄、目录及会话管理等功能。它适合棚拍、人像、商业产品、婚礼和风光摄影,

Adobe Acrobat Pro Windows版
Adobe Acrobat Pro Windows版

Adobe Acrobat Pro 是面向专业 PDF 工作的综合解决方案,可在 Windows 中创建、编辑、转换、比较、整理和保护文档,并支持表单、电子签名、扫描件文字识别及敏感信息永久遮盖。订阅还包含网页、移动服务和云端协作能力,

Adobe After Effects Windows版
Adobe After Effects Windows版

Adobe After Effects 是面向动态图形、动画与影视视觉特效制作的专业工具,可完成关键帧动画、图层合成、遮罩跟踪、抠像、文字动画和二维三维元素整合。Windows 版通过 Adobe Creative Cloud 安装及更新,

DaVinci Resolve Windows版
DaVinci Resolve Windows版

DaVinci Resolve是Blackmagic Design推出的一体化影视后期工具,将素材管理、非线性剪辑、专业调色、Fusion视觉特效、Fairlight音频处理和成片交付整合在统一工程中。免费版已覆盖多数个人创作需求,