前言:
笔者的实习使用.NET环境而之前在学校给培训的是Java,
所以目前经常在两种语言和平台中切换,
由于工作中使用C#,学到了很多C#的知识和框架,它们或多或少和Java中有的东西有相似性.
从中也有一些启发,故将之记录下来
背景:
C#的Linq最早发布于2007年的C#3.0
而Java的流操作到Java8标准才出现
两者的使用目的都是为了简化或提高对集合类型(广义的IEumnable对象)的操作,提高效率.
且都被实践证明是非常重要的.
理解和掌握他们的使用对自己的编程思想是一次大的提升
接下来是具体的使用
Java的流操作
Java的流操作主要操作集合类型(当然也可以是数组),方式分为并行操作和串行操作(并行操作涉及多线程进行)
这里举一个简单的例子
public class StreamTest {
//初始化一个简单的字符串集合
private static List<String> list;
static {
list = new ArrayList<>();
list.add("GTX1060");
list.add("GTX1070");
list.add("6800XT");
list.add("RTX3090");
list.add("RTX3060");
list.add("6500XT");
list.add("HD7850");
}
public static void main(String[] args) {
//利用简单的Filter过滤,以下两种方法都能够把N卡从集合中挑选出来
List<String> fuckNvidia = list.stream()
.filter(item->!item.endsWith("XT")&&!item.startsWith("HD")).toList();
List<String> fuckNvidia2 = list.stream()
.filter(item->item.startsWith("GTX")||item.startsWith("RTX")).toList();
fuckNvidia.forEach(item->System.out.println(item));
//结果
//GTX1060
//GTX1070
//RTX3090
//RTX3060
//map方法以便利处理集合中每一个元素,将其变成小写
//这里要理解什么是Function<T,R>,Predicate<T>,在Java中,它们是动态的接口
//在C#中它们是函数委托可以直接赋值匹配的方法,这里有一个区别就是C#的Function可以无限的塞参数,像Funaction<T,T,T,T,...,R>而Java不行,BiFunction也只能输入两个参数,并不是很自由
List<String> lower = list.stream().map((item)-> {return item.toLowerCase();}).toList();
lower.forEach(item->System.out.println(item));
//结果:
//gtx1060
//gtx1070
//6800xt
//rtx3090
//rtx3060
//6500xt
//hd7850
}
}
再来个稍微复杂点的例子
public class StreamPlusTest {
private static List<Person> personlist;
static {
personlist = new ArrayList<Person>();
personlist.add(new Person("黄仁勋", "呆湾省", 50, "屑"));
personlist.add(new Person("拿破仑", "科西嘉", 250, "FRA"));
personlist.add(new Person("弗朗茨", "维也纳", 270, "HRE"));
personlist.add(new Person("达武", "法兰西", 255, "FRA"));
personlist.add(new Person("腓特烈", "柏林", 255, "HRE"));
}
//这次来操作对象集合
public static class Person{
private String name;
private String hometown;
private int age;
private String Work;
public Person() {
}
public Person(String name, String hometown, int age, String work) {
this.name = name;
this.hometown = hometown;
this.age = age;
Work = work;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHometown() {
return hometown;
}
public void setHometown(String hometown) {
this.hometown = hometown;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getWork() {
return Work;
}
public void setWork(String work) {
Work = work;
}
@Override
public String toString() {
return "Person [name=" + name + ", hometown=" + hometown + ", age=" + age + ", Work=" + Work + "]";
}
}
public static void main(String[] args) {
//根据实体的某个属性赛选集合内容
List<Person> list = personlist.stream().filter(person->person.getWork().equals("FRA")).toList();
list.forEach(item->System.out.println(item));
//得到如下输出
//Person [name=拿破仑, hometown=科西嘉, age=250, Work=FRA]
//Person [name=达武, hometown=法兰西, age=255, Work=FRA]
//根据某个属性的值对集合进行排序
List<Person> sorted_list = personlist.stream()
.sorted((left,right)->left.getAge()>right.getAge()? 1:-1).toList();
sorted_list.forEach(item->System.out.println(item));
//进行reduce计算,返回计算结果,PS:不要对reduce名字的意思迷惑,它确实负责进行计算,并非字面上的减少/减轻的含义
//这里需要注意这个BinaryOperator,它是二元运算符的意思,它在这里负责制定运算的规则
Optional<Person> p1 = personlist.stream().reduce(new BinaryOperator<StreamPlusTest.Person>() {
@Override
public Person apply(Person t, Person u) {
Person temp = new Person();
temp.setAge(t.getAge()+u.getAge());
return temp ;
}
});
System.out.println(p1.get());
//输出累计年龄值1080
}
}
stream操作确实有一定的难度,尤其是涉及并行的计算,还有方法执行的嵌套.
C#的LINQ操作
linq的语法很像Sql,如果你熟悉Sql的话上手并不是很难
并且,它的拓展性也很好,可以被转义成sql执行,配合EF也使ORM的操作变得简单
这里以和上例一样的数据进行举例
class Program
{
private static List<string> list;
public static List<string> ListINIT()
{
return new List<string>()
{
"GTX1060",
"GTX1070",
"6800XT",
"RTX3090",
"RTX3060",
"6500XT",
"HD7850"
};
}
static void Main(string[] args)
{
list = ListINIT();
//LINQ可以使用两种查询模式,它们差别比较大,但效果是一样的,你可以任选其一
//1.方法调用方式
List<string> list1 = list.Where(item=>item.StartsWith("GTX")||item.StartsWith("RTX")).ToList();
//查询语句方式
List<string> list2 = (from item in list where item.StartsWith("RTX") || item.StartsWith("GTX") select item).ToList();
//可以用select方法对集合内的数据进行操作,但这不是这要用途,主要用途是用于圈定一个自己指定返回类型
//这个在之后的用例中将会讲到
List<string> list3 = list.Select(item => item = item.ToLower()).ToList();
}
}
这里这个Where就和Java的Stream中的filter一样,也对集合进行了过滤的操作
当然,LINQ也可以进行集合数据的操作,
为了对比,再将上面的例子放入C#中
public class Person
{
public string name { get; set; }
public string hometown { get; set; }
public int age { get; set; }
public string work { get; set; }
public Person(string name, string hometown, int age, string work)
{
this.name = name;
this.hometown = hometown;
this.age = age;
this.work = work;
}
public override string ToString()
{
return "Person["+name+","+hometown+","+age+","+work+"]";
}
}
class Program
{
public static List<Person> ListInitTwo()
{
return new List<Person>() {
new Person("黄仁勋", "呆湾省", 50, "屑"),
new Person("拿破仑", "科西嘉", 250, "FRA"),
new Person("弗朗茨", "维也纳", 270, "HRE"),
new Person("达武", "法兰西", 255, "FRA"),
new Person("腓特烈", "柏林", 255, "HRE")
};
}
public static int CompareByAge(Person x, Person y)
{
return x.age > y.age ? 1 : -1;
}
static void Main(string[] args)
{
List<Person> list = ListInitTwo();
//同样可以通过Where进行过滤集合
List<Person> list2 = list.Where(item => item.work.StartsWith("FRA")).ToList();
//可以使用委托来进行排序,
//list.Sort(CompareByAge);
//也可以通过lambda进行排序,实现方法都是实现IComparer这个比较借口,这个IComparable和IComparer借口和Java的设计一模一样,有兴趣可以深入了解一下
list.Sort((x, y) => { return x.age > y.age ? 1 : -1; });
list.ForEach(item => Console.WriteLine(item));
//可以调用这个aggregate进行计算,和stream操作的reduce差不多,这个地方也是比较复杂的,需要深入的思考,这里只做一个小的演示
var result = list.Aggregate((x,y)=>{
Person temp = new Person();
temp.age = x.age + y.age;
return temp;
});
Console.WriteLine("合計的年齡為"+result);
}
}