Java8 Lamda的基本使用
Lamda的基本使用
https://www.cnblogs.com/htyj/p/10133883.html
https://segmentfault.com/q/1010000011200292
1、Collection
Java 8 为Iterable接口新增了一个forEach(Consumer action)默认方法,该方法所需参数的类型是一个函数式接口,而Iterable接口是Collection接口的父接口,因此Collection集合也可以直接调用该方法。
当程序调用Iterable的forEach(Consumer action)遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。正因为Consumer是函数式接口,因此可以使用Lambda表达式来遍历集合元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import java.util.Collection; import java.util.HashSet; import java.util.function.*; public class CollectionEach{ public static void main(String []args){ //创建一个集合 Collection<String> c= new HashSet<String>(); //需要泛型,否则提示警告:使用了未经检查或不安全的操作,可以直接运行 c.add( "ni" ); c.add( "hao" ); c.add( "java" ); c.forEach( new Consumer<String>(){ public void accept(String t){ System.out.println( "集合元素是:" +t); } }); //不使用lambda表达式和两种使用lambda表达式方式 c.forEach(t->System.out.println( "集合元素是:" +t)); c.forEach(System.out::println); } } |
2、Iterator
java 8 为Iterator新增了一个forEachRemaining(Consumer action)方法,该方法所需的Consumer参数同样也是函数式接口。当程序调用Iterator的forEachRemaining(Consumer action)遍历集合元素时,程序会一次将集合元素传递给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import java.util.Collection; import java.util.Iterator; import java.util.HashSet; public class IteratorEach{ public static void main(String []args){ Collection<String> c= new HashSet<String>(); c.add( "ni" ); c.add( "hao" ); c.add( "java" ); Iterator<String> it=c.iterator(); //使用泛型 it.forEachRemaining(System.out::println); } } |
forEach的使用
1
2
|
Random random = new Random(); random.ints().limit( 10 ).forEach(System.out::println); |
但是forEach并没有返回值,所以有时候在对集合进行循环,使用map()函数更为方便:
final List<String> friends = Arrays.asList("Brian", "Nate", "Neal", "Raju", "Sara", "Scott");
friends.stream().map(name -> name.toUpperCase()).forEach(name -> System.out.print(name + " "));
System.out.println();
老式遍历和新式遍历的区别:
java8之前的增强for这种方式的迭代是使用Iterator接口来实现的,调用了它的hasNext和next方法。 这两种方式都属于外部迭代器,它们把如何做和想做什么揉到了一起。我们显式的控制迭代,告诉它从哪开始到哪结束;第二个版本则在底层通过Iterator的方法来做这些。显式的操作下,还可以用break和continue语句来控制迭代。 第二个版本比第一个少了点东西。如果我们不打算修改集合的某个元素的话,它的方式比第一个要好。不过这两种方式都是命令式的,在现在的Java中应该摒弃这种方式。 改成函数式原因有这几个:
- for循环本身是串行的,很难进行并行化。
- 这样的循环是非多态的;所得即所求。我们直接把集合传给for循环,而不是在集合上调用一个方法(支持多态)来执行特定的操作。
- 从设计层面来说,这样 写的代码违反了“Tell,Don't Ask”的原则 。我们请求执行一次迭代,而不是把迭代留给底层库来执行。
是时候从老的命令式编程转换到更优雅的内部迭代器的函数式编程了。使用内部迭代器后我们把很多具体操作都扔给了底层方法库来执行,你可以更专注于具体的业务需求。底层的函数会负责进行迭代的。我们先用一个内部迭代器来枚举一下名字列表。
Iterable接口在JDK8中得到加强,它有一个专门的名字叫forEach,它接收一个Comsumer类型的参数。如名字所说,Consumer的实例正是通过它的accept方法消费传递给它的对象的。
这个forEach方法是一个高阶函数,它接收一个lambda表达式或者代码块,来对列表中的元素进行操作。在每次调用的时候 ,集合中的元素会绑定到name这个变量上。底层库托管了lambda表达式调用的活。它可以决定延迟表达式的执行,如果合适的话还可以进行并行计算。内部迭代器的版本更为简洁。而且,使用它的话我们可以更专注每个元素的处理操作,而不是怎么去遍历——这可是声明式的。
不过这个版本还有缺陷。一旦forEach方法开始执行了,不像别的两个版本,我们没法跳出这个迭代。(当然有别的方法能搞定这个)。
在这个例子里,Java编译器通过上下文分析,知道name的类型是String。它查看被调用方法forEach的签名,然后分析参数里的这个函数式接口。接着它会分析这个接口里的抽象方法,查看参数的个数及类型。即便这个lambda表达式接收多个参数,我们也一样能进行类型推导,不过这样的话所有参数都不能带参数类型;在lambda表达式中,参数类型要么全不写,要写的话就得全写。
Java编译器对单个参数的lambda表达式会进行特殊处理:如果你想进行类型推导的话,参数两边的括号可以省略掉。
这里有一点小警告:进行类型推导的参数不是final类型的。在前面显式声明类型例子中,我们同时也把参数标记为final的。这样能防止你在lambda表达式中修改参数的值。通常来说,修改参数的值是个坏习惯,这样容易引起BUG,因此标记成final是个好习惯。不幸的是,如果我们想使用类型推导的话,我们就得自己遵守规则不要修改参数,因为编译器可不再为我们保驾护航了。
fiter过滤器的使用
示例:

List<Person> javaProgrammers = new ArrayList<Person>() {
{
add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
}
};

- 单个条件过滤
1
2
|
String s = comRepayInfo.getLoanOrderId() + String.valueOf(repayNum1); List<CompensatVO> collect = temporaryList.stream().filter((CompensatVO vo1) -> (vo1.getLoanOrderId()+vo1.getRepayNum()).equals(s)).collect(Collectors.toList()); |
- 多条件重过滤

// 定义 filters
Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender())); System.out.println("下面是年龄大于 24岁且月薪在$1,400以上的女PHP程序员:");
phpProgrammers.stream()
.filter(ageFilter)
.filter(salaryFilter)
.filter(genderFilter)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("最前面的3个女性 Java programmers:");
javaProgrammers.stream()
.filter(genderFilter)
.limit(3)
.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
System.out.println("根据 name 排序,并显示前5个 Java programmers:");
List<Person> sortedJavaProgrammers = javaProgrammers
.stream()
.sorted((p, p2) -> (p.getFirstName().compareTo(p2.getFirstName())))
.limit(5)
.collect(toList());

- 选择某个字段最大的和最小的值
使用这种方法不需要先排序

System.out.println("工资最低的 Java programmer:");
Person pers = javaProgrammers
.stream()
.min((p1, p2) -> (p1.getSalary() - p2.getSalary()))
.get();
System.out.printf("Name: %s %s; Salary: $%,d.", pers.getFirstName(), pers.getLastName(), pers.getSalary()) System.out.println("工资最高的 Java programmer:");
Person person = javaProgrammers
.stream()
.max((p, p2) -> (p.getSalary() - p2.getSalary()))
.get();
System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary())

List排序:
- 升序排序:
1
2
3
|
1 .Collections.sort(dataList,(p1,p2)->p1.getInstallmentNo().compareTo(p2.getInstallmentNo())); //dataList是需要排序的集合 2 .Collections.sort(dataList,Comparator.comparing(RepayPlanDTO::getInstallmentNo)); //dataList是需要排序的集合 3 .dataList.stream.sorted(Comparator.comparing((Apple a) -> a.getWeight())).collect(Collectors.toList()); <br> //或者等价于 <br> dataList.stream.sorted(Comparator.comparing(Apple::getWeight)).collect(Collectors.toList());//dataList是需要排序的集合 |
1
|
<em id= "__mceDel" > 4 .userList.stream.sort((String s1, String s2) -> (s1.length() - s2.length())); //根据名称的长度进行排序</em> |
- 降序排序
1
2
|
1 .Collections.sort(dataList,(p1,p2)->p2.getInstallmentNo().compareTo(p1.getInstallmentNo())); 2 .dataList.stream.sorted(Comparator.comparing(Apple::getWeight).reversed()).collect(Collectors.toList()); |
- 多参数排序
1
|
1 .dataList.sort(Comparator.comparing(RepayPlanDetailFileVO::getLoanOrderId).thenComparing(RepayPlanDetailFileVO::getRepayNum)); |
list转map的操作
示例:

List<User> list =new ArrayList<>();
User user1=new User("fgg",2);
User user2=new User("ghj",1);
User user3=new User("dft",4);
User user4=new User("abc",3);
User user5=new User("ads",5);
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
list.add(user5);

- 用年龄作为map的key,名字作为map的value:
Map<Integer, User> collect3 = collect2.stream().collect(Collectors.toMap(User::getAge, User:getName));
- 用年龄作为map的key,user对象作为map的value:
Map<Integer, User> collect3 = collect2.stream().collect(Collectors.toMap(User::getAge, user -> user));
Map<Integer, User> collect5 = collect2.stream().collect(Collectors.toMap(User::getAge, Function.identity()));这两种用法是等价的
- 但是如果做为key的字段有重复的上面的写法就不行了,在执行程序时就会报错,但我们可以用下面的方式来解决:
Map<Integer, User> collect3 = collect2.stream().collect(Collectors.toMap(User::getAge, user -> user,(key1, key2) -> key2));//这种写法只是在出现两个一样的key值时,暴力的用后面一个key的value值覆盖前面相同的key的value值
Map<Integer, List<User>> collect5 = collect2.stream().collect(Collectors.groupingBy(User::getAge));//这种写法是当遇到相同的key值时,会自动的将相同key值的value值组装成一个集合
Map<Integer, User> collect3 = collect2.stream().collect(Collectors.toMap(User::getAge, user -> user,(key1, key2) -> key2,LinkedHashMap::new));//这种写法是我们可以执行特定的map类型来接受我们的结果
- 使用map()函数进行转换

System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
String phpDevelopers = phpProgrammers
.stream()
.map(Person::getFirstName)
.collect(joining(" ; ")); // 在进一步的操作中可以作为标记(token) System.out.println("将 Java programmers 的 first name 存放到 Set:");
Set<String> javaDevFirstName = javaProgrammers.stream().map(Person::getFirstName).collect(Collectors.toSet());
System.out.println("将 Java programmers 的 first name 存放到 TreeSet:"); TreeSet<String> javaDevLastName = javaProgrammers .stream() .map(Person::getLastName) .collect(toCollection(TreeSet::new));

Map排序:
1
2
3
|
Map<String ,Integer> map= new HashMap<>(); LinkedHashMap<String, Integer> collect = map.entrySet().stream().sorted(Map.Entry.<String, Integer>comparingByValue()).collect(Collectors.toMap(k -> k.getKey(), v -> v.getValue(), (k, v) -> k, LinkedHashMap:: new )); |
public Map<String, Integer> sortedByKeys(Map<String, Integer> map) {
Map<String, Integer> result = new LinkedHashMap<>();
map.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEachOrdered(x -> result.put(x.getKey(), x.getValue()));
return result;
}
- comparingByKey() //利用key值进行排序,但要求key值类型需要实现Comparable接口。
- comparingByValue() //利用value值进行排序,但要求key值类型需要实现Comparable接口。
- comparingByKey(Comparator) //利用key值进行排序,但key值并没有实现Comparable接口,需要传入一个Comparator比较器。
- comparingByValue(Comparator) //利用value值进行排序,但value值并没有实现Comparable接口,需要传入一个Comparator比较器。
Map合并
示例:

Map<String, Employee> map1 = new HashMap<>();
Map<String, Employee> map2 = new HashMap<>();
Employee employee1 = new Employee(1L, "Henry");
Employee employee2 = new Employee(22L, "Annie");
Employee employee3 = new Employee(8L, "John");
map1.put(employee1.getName(), employee1);
map1.put(employee2.getName(), employee2);
map1.put(employee3.getName(), employee3); Employee employee4 = new Employee(2L, "George");
Employee employee5 = new Employee(3L, "Henry");
map2.put(employee4.getName(), employee4);
map2.put(employee5.getName(), employee5);
Map<String, Employee> map3 = new HashMap<>(map1);

- 使用Merge将map2合并到map3中:
Java8为 java.util.Map接口新增了merge()函数。merge() 函数的作用是: 如果给定的key之前没设置value 或者value为null, 则将给定的value关联到这个key上.否则,通过给定的remaping函数计算的结果来替换其value。如果remapping函数的计算结果为null,将解除此结果。First, let’s construct a new HashMap by copying all the entries from the map1:
map2.forEach(
(key, value) -> map3.merge(key, value, (v1, v2) -> new Employee(v1.getId(),v2.getName()))
);
其中(v1, v2) -> new Employee(v1.getId(),v2.getName())是当出现key相同时的合并策略,示例中的key值出现相同的情况时的合并策略是:id来自map3而name来自map2.
- 使用Stream.concat()进行合并:
Java8的Stream API 也为解决该问题提供了较好的解决方案。
Map<String, Employee> result=Stream.concat(map1.entrySet().stream(), map2.entrySet().stream()).collect( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,(value1, value2) -> new Employee(value2.getId(), value1.getName())));;
- 使用Stream.of()进行合并:
通过Stream.of()方法不需要借助其他stream就可以实现map的合并。
Map<String, Employee> map3 = Stream.of(map1, map2).flatMap(map -> map.entrySet().stream()).collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1, v2) -> new Employee(v1.getId(), v2.getName())));
- 使用Simple Streaming进行合并:
借助stream的管道操作来实现map合并
Map<String, Employee> map3 = map2.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey,Map.Entry::getValue,(v1, v2) -> new Employee(v1.getId(), v2.getName()),() -> new HashMap<>(map1)));
- 使用StreamEx进行合并:
我们还可以使Stream API 的增强库
Map<String, Employee> map3 = EntryStream.of(map1).append(EntryStream.of(map2)).toMap((e1, e2) -> e1);
注意:(e1, e2) -> e1 表达式来处理重复key的问题,如果没有该表达式依然会报IllegalStateException异常.
Predicate和Function的使用
目前,已经出现了两种类型的函数式接口(Functional Interface)。它们分别是filter方法使用的Predicate和map方法使用的Function。
在上面我们讲filter的多条件过滤时,我们把过滤的条件单独拿了出来,可以方便方法内的其他filter复用,但是如果我们在进行某种过滤时只是传的值不通过滤条件是相同的,遇到这种情况我们是否需要再增加个过滤条件呢,很显然要是这样做了重复代码就太多了,我们可以如下来做:
第一种做法:
public static Predicate<Integer> checkAge(final Integer letter) {
return (p) -> (p.getAge() >letter);
}
第二种方式:
final Function<Integer, Predicate<Integer>> checkAgeResult= (Integer letter) -> { Predicate<Integer> checkAge = (Integer age) -> age>letter; return checkAge; };
或者
final Function<Integer, Predicate<Integer>> checkAgeResult = (Integer letter) -> (Integer age) -> age>letter;
或者
final Function<Integer, Predicate<Integer>> checkAgeResult = letter -> age -> age>letter;
这三者是等价的
使用示例:
final Function<Integer, Predicate<User>> checkAge = letter -> age -> age.getAge()>letter;
final Function<String, Predicate<User>> checkName = letter -> name -> name.getName().contains(letter);
List<User> abc = list.stream().filter(checkAge.apply(2).and(checkName.apply("abc"))).collect(Collectors.toList());
注意:在使用的时候Function<Integer,President<User>>中的User对应的就是最终集合中的类型,而Integer对应的是参数“letter”的数据类型
计算
mapToInt方法得到的是一个Stream类型的子类型IntStream的实例,与IntStream类似,还有LongStream和DoubleStream类型,用于简化相关的操作。而通常和这些方法联合使用的除了sum()方法还有max(),min(),average()等一系列方法用来实现常用的归约。
reduce方法的工作原理,可以这样概括:在对一个集合中的元素按照顺序进行两两操作时,根据某种策略来得到一个结果,得到的结果将作为一个元素参与到下一次操作中,最终这个集合会被归约成为一个结果。这个结果也就是reduce方法的返回值。
- 计算支付金额的总和,可以使用并行Stream提高效率
System.out.println("计算付给 Java programmers 的所有money:");
int totalSalary = javaProgrammers
.parallelStream()
.mapToInt(p -> p.getSalary())
.sum();
- 获取某个数据的统计数据

//计算 count, min, max, sum, and average for numbers
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
IntSummaryStatistics stats = numbers
.stream()
.mapToInt((x) -> x)
.summaryStatistics(); System.out.println("List中最大的数字 : " + stats.getMax());
System.out.println("List中最小的数字 : " + stats.getMin());
System.out.println("所有数字的总和 : " + stats.getSum());
System.out.println("所有数字的平均值 : " + stats.getAverage());

- reduce归约

final Optional<String> aLongName = friends.stream()
.reduce((name1, name2) ->
name1.length() >= name2.length() ? name1 : name2);
aLongName.ifPresent(name -> System.out.println(String.format("A longest name: %s", name)));
第一次执行两两操作时,name1和name2代表的是集合中的第一个和第二个元素,当第一个元素的长度大于等于第二个元素时,将第一个元素保留下来,否则保留第二个元素。 第二次执行两两操作时,name1代表的是上一次操作中被保留下来的拥有较长长度的元素,name2代表的是第三个元素。 以此类推...最后得到的结果就是集合中第一个拥有最长长度的元素了。

实际上,reduce方法接受的Lambda表达式的行为被抽象成了BinaryOperator接口:

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
// others...
} @FunctionalInterface
public interface BiFunction<T, U, R> { /**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u); // others...
}

源码也反映了BinaryOperator和另一个函数式接口BiFunction之间的关系,当BiFunction接口中接受的三个参数类型一致时,也就成为了一个BinaryOperator接口。因此,前者实际上是后者的一个特例。
另外需要注意的几点:
- reduce方法返回的对象类型时Optional,这是因为待操作的集合可能是空的。
- 当集合只有一个元素时,reduce会立即将该元素作为实际结果以Optional类型返回,不会调用传入的Lambda表达式。
- reduce方法是会按照集合的顺序对其元素进行两两操作的,可以额外传入一个值作为“基础值”或者“默认值”,那么在第一次进行两两操作时,第一个操作对象就是这个额外传入的值,第二个操作对象是集合中的第一个元素。
比如,以下代码为reduce方法传入了默认值:
final String steveOrLonger =
friends.stream()
.reduce("Steve", (name1, name2) ->
name1.length() >= name2.length() ? name1 : name2);
- 复制不同的值
// 用所有不同的数字创建一个正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s, Square Without duplicates : %s %n", numbers, distinct);
- 元素链接
// 将字符串换成大写并用逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
或者
String result=names.stream().map(String::toUpperCase).collect(Collectors.joining(", "));
可见collect方法并不自己完成归约操作,它会将归约操作委托给一个具体的Collector,而Collectors类型则是一个工具类,其中定义了许多常见的归约操作,比如上述的joining Collector
学习链接:
java8中Comparable与Comparator的区别
java8新特性 lambda Stream map(函数式编程)
______________________________________________________________________________________________________________________________
//记录一下相关的转换,以备后续学习
public static void main(String[] args){
String[] a = stripAll("1","2","3","4");//org.apache.commons.lang3.StringUtils
String[] b = stripAll(new String[]{"1","2","3","4"},"/");
System.out.println(strip("/123/123 /","/"));
System.out.println(Arrays.toString(a));
String start = "'";
String end = "'";
String link = ",";
int[] arr = {1,2,3,4};
String[] arr2 = {"1","2","3","4"};
//forEach函数实现内部迭代
StringBuilder sb = new StringBuilder();
Arrays.asList(arr2).forEach(o -> { if (sb.length() > 0) {sb.append(link);} sb.append(start + o + end);});//forEach函数实现内部迭代
System.out.println(sb);
//map collect
String str1 = Arrays.stream(arr).boxed().map(i -> start+i.toString()+end).collect(Collectors.joining(link));
String str1_1 = Arrays.stream(arr2).map(i -> start+i.toString()+end).collect(Collectors.joining(link));
System.out.println("str1:"+str1);
System.out.println("str1_1:"+str1_1);
//map reduce
String str2 = Arrays.stream(arr).boxed().map(i -> start+i.toString()+end).reduce("/", String::concat);
System.out.println("str2:"+str2);
// 方法引用Object::toString
String str3 = Arrays.stream(arr).boxed().map(Object :: toString).reduce("/", String::concat);
System.out.println("str3:"+str3);
}
————————————————
版权声明:本文为CSDN博主「曼_雨_馨_渼」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hr952909686/article/details/80678316
Java8 Lamda的基本使用的更多相关文章
- java8 lamda快速入门
Lambda语法详解 我们在此抽象一下lambda表达式的一般语法: 1 (Type1 param1, Type2 param2, ..., TypeN paramN) -> { 2 sta ...
- Java8 lamda表达式快速上手
1.对比着经典foreach 简单的循环 o相当于foreach中的临时变量,要遍历的list放在句首 list.foreach(o->{你要进行的操作}); package com.compa ...
- java8 lamda 表达式
- Java8 CompletableFuture
http://colobu.com/2016/02/29/Java-CompletableFuture/ http://www.deadcoderising.com/java8-writing-asy ...
- jdk版本特性
https://segmentfault.com/a/1190000004419611 java5 泛型 枚举 装箱拆箱 变长参数 注解 foreach循环 静态导入 格式化 线程框架/数据结构 Ar ...
- java8的新特性详解-----------Lamda表达式
java8最大的亮点就是引入了Lamda表达式 , 函数式编程的概念 具体啥意思我也不知道.只管用就行了,非常的强大,简洁,一个表达式相当于以前的十几行代码 因为之前要实现这种效果全靠if el ...
- java8 使用 lamda 表达式 完成 map reduce
java8支持了函数编程,可以让java代码更简洁和易读. 传统 for 循环方式: List<String> list = Arrays.asList("C",&qu ...
- JAVA8新特性——Lamda表达式
JAVA9都要出来了,JAVA8新特性都没搞清楚,是不是有点掉队哦~ Lamda表达式,读作λ表达式,它实质属于函数式编程的概念,要理解函数式编程的产生目的,就要先理解匿名内部类. 先来看看传统的匿名 ...
- java8按照lamda表达式去重一个list,根据list中的一个元素
/** * 按照指定字段给list去重 * @param list * @return */ public static List<DataModel> niqueList(List< ...
随机推荐
- Android四大组件:BroadcastReceiver 介绍
介绍 BroadcastReceiver 即广播组件,是 Android 的四大组件之一.用于监听和接收广播消息,并做出响应.有以下一些应用: 不同组件之间的通信(应用内或不同应用之间). 多线程之间 ...
- jmeter连接并使用mysql数据
一.下载数据库驱动,放至D:\apache-jmeter-2.13\lib\ext目录下 二.打开jmeter,右键添加->配置文件->JDBC Connection Configurat ...
- 其他综合-VMware 从模板机快速克隆多台
VMware 从模板机快速克隆多台 1.实验描述 通过 CentOS 7.6 的模板机快速克隆,为实现搭建其他项目而提供干净的实验平台. [基于此文章的环境]点我快速打开文章 2.实验环境 使用软件的 ...
- Paxos算法—前世
Paxos算法是基于消息传递且具有高度容错特性的一致性算法.我们将从一个简单的问题开始,逐步的改进我们的设计方案,最终得到Paxos,一个可以在逆境下工作的协议. 一.客户端-服务器模型 我们从最小的 ...
- MLflow安装配置初入门
学习这个时,要和Kubeflow作比较, 看看它们俩在解决和规范机器学习流程方面的思路异同. mlflow三大内涵: Tracking, Projects, Models. 一,基础镜像 harbor ...
- docker-nginx
docker pull nginx docker run --name nginx -p 8080:80 -d nginx mkdir -p /data/nginx/www /data/nginx/l ...
- JAVA并发-ReentrantReadWriteLock
简介 读写锁维护着一对锁,一个读锁和一个写锁.通过分离读锁和写锁,使得并发性比一般的排他锁有了较大的提升:在同一时间可以允许多个读线程同时访问,但是在写线程访问时,所有读线程和写线程都会被阻塞. 读写 ...
- ABP 下载源码报错
ASP.NET Boilerplate 下载地址应该是这个:https://github.com/aspnetboilerplate/aspnetboilerplate/tree/v1.5.2 下载的 ...
- [LeetCode] 505. The Maze II 迷宫之二
There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolli ...
- Solidity智能合约语言
语言本身 ethereum官网 https://ethereum.org/zh/ 笔记 uint[] result = new uint[](3); uint[] memory result = ...