Java 8 Stream 由多个字段区分

通过比较Java 8中的多个字段或属性,学习从不同对象的 Stream 收集不同对象

1.由多个字段区分–distinctByKeys()函数

下面给出的是一个接受varargs参数的函数,我们可以传递多个键提取器(我们要在其上过滤重复项的字段)。

此函数创建一个键,List因为list包含用于检查不同组合的字段的值。列表键插入到,ConcurrentHashMap其中仅存储唯一且不同的键。

uniqueByKeys()函数
private static <T> Predicate<T> distinctByKeys(Function<? super T, ?>..。 keyExtractors)
{
  final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
  
  return t ->
  {
    final List<?> keys = Arrays.stream(keyExtractors)
                .map(ke -> ke.apply(t))
                .collect(Collectors.toList());
    
    return seen.putIfAbsent(keys, Boolean.TRUE) == null;
  };
}

在给定的示例中,我们发现所有具有的记录distinct ids and name。我们应该只有3条记录作为输出。

Main.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
  public static void main(String[] args)
  {
    List<Record> recordsList = getRecords();
    List<Record> list = recordsList
                .stream()
                .filter(distinctByKeys(Record::getId, Record::getName))
                .collect(Collectors.toList());
    System.out.println(list);
  }
  private static <T> Predicate<T> distinctByKeys(Function<? super T, ?>..。 keyExtractors)
  {
    final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>();
    
    return t ->
    {
      final List<?> keys = Arrays.stream(keyExtractors)
                  .map(ke -> ke.apply(t))
                  .collect(Collectors.toList());
      
      return seen.putIfAbsent(keys, Boolean.TRUE) == null;
    };
  }
  private static ArrayList<Record> getRecords()
  {
    ArrayList<Record> records = new ArrayList<>();
    records.add(new Record(1l, 10l, "record1""record1@email.com""India"));
    records.add(new Record(1l, 20l, "record1""record1@email.com""India"));
    records.add(new Record(2l, 30l, "record2""record2@email.com""India"));
    records.add(new Record(2l, 40l, "record2""record2@email.com""India"));
    records.add(new Record(3l, 50l, "record3""record3@email.com""India"));
    return records;
  }
}

程序输出。

安慰
[
  Record [id=1, count=10, name=record1, email=record1@email.com, location=India],
  Record [id=2, count=30, name=record2, email=record2@email.com, location=India],
  Record [id=3, count=50, name=record3, email=record3@email.com, location=India]
]

2.按多个属性区分–自定义键类

另一种可能的方法是使用一个自定义类,该类代表POJO类的不同键。在我们的案例中,我们创建了一个CustomKey类,该类可以具有任意多个字段,并且列表中的不同元素将基于所有这些字段的值的不同组合来获取。

在给定的示例中,我们再次发现所有具有唯一键和名称的记录。

CustomKey.java
public class CustomKey
{
  private long id;
  private String name;
  
  public CustomKey(final Record record)
  {
    this.id = record.getId();
    this.name = record.getName();
  }
  
  //Getters and setters
  
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + (int) (id ^ (id >>> 32));
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
  }
  
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    CustomKey other = (CustomKey) obj;
    if (id != other.id)
      return false;
    if (name == null) {
      if (other.name != null)
        return false;
    } else if (!name.equals(other.name))
      return false;
    return true;
  }
}

让我们看看如何使用此自定义键基于给定的多个字段过滤列表中的不同元素。

Main.java
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
  public static void main(String[] args)
  {
    List<Record> recordsList = getRecords();
    List<Record> list = recordsList.stream()
              .filter(distinctByKey(CustomKey::new))
              .collect(Collectors.toList());
    System.out.println(list);
  }
  public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor)
  {
    Map<Object, Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
  }
  private static ArrayList<Record> getRecords()
  {
    ArrayList<Record> records = new ArrayList<>();
    records.add(new Record(1l, 10l, "record1""record1@email.com""India"));
    records.add(new Record(1l, 20l, "record1""record1@email.com""India"));
    records.add(new Record(2l, 30l, "record2""record2@email.com""India"));
    records.add(new Record(2l, 40l, "record2""record2@email.com""India"));
    records.add(new Record(3l, 50l, "record3""record3@email.com""India"));
    return records;
  }
}

程序输出。

安慰
[
  Record [id=1, count=10, name=record1, email=record1@email.com, location=India],
  Record [id=3, count=30, name=record2, email=record2@email.com, location=India],
  Record [id=5, count=50, name=record3, email=record3@email.com, location=India]
]

作为参考,我们使用了下面给出的Record类。

Record.java
public class Record
{
  private long id;
  private long count;
  private String name;
  private String email;
  private String location;
  
  public Record(long id, long count, String name,
                  String email, String location) {
    super();
    this.id = id;
    this.count = count;
    this.name = name;
    this.email = email;
    this.location = location;
  }
  
  //Getters and setters
  @Override
  public String toString() {
    return "Record [id=" + id + ", count=" + count + ", name=" + name +
                    ", email=" + email + ", location="
                    + location + "]";
  }
}

阅读更多:

Java 8 –从 Stream 中获取不同的对象

saigon has written 1445 articles

Leave a Reply