java 8 如何获取多个属性的不同列表
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/42817884/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
java 8 how to get distinct list on more than one property
提问by rcipher222
How can one get the distinct (distinct based on two property) list from a list of objects.
for example let there are list of objects with property name and price.
Now how can I get a list with distinct name or price.
suppose
如何从对象列表中获取不同的(基于两个属性的不同)列表。例如让有属性名称和价格的对象列表。现在如何获得具有不同名称或价格的列表。
认为
list<xyz> l1 = getlist(); // getlist will return the list.
Now let l1 has the following properties(name, price) :-
n1, p1
n1, p2
n2, p1
n2, p3
现在让 l1 具有以下属性(名称,价格):-
n1, p1
n1, p2
n2, p1
n2, p3
Now after the filter the list should be-
n1, p1
n2, p3
现在过滤器之后的列表应该是 -
n1, p1
n2, p3
I tried solving like this -
我试过这样解决 -
public List<xyz> getFilteredList(List<xyz> l1) {
return l1
.stream()
.filter(distinctByKey(xyz::getName))
.filter(distinctByKey(xyz::getPrice))
.collect(Collectors.toList());
}
private 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;
}
Now the problem is when i did filter on name the list return would be -
n1, p1
n2, p1
现在的问题是当我对名称进行过滤时,列表返回将是 -
n1, p1
n2, p1
and then it would have run filter on price which return -
n1, p1
然后它会在返回的价格上运行过滤器 -
n1, p1
which is not the expected result.
这不是预期的结果。
回答by Andreas
Almost verbatim from Stuart Marks' answer:
Stuart Marks 的回答几乎是逐字逐句的:
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;
class Class {
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 List<Pojo> getList() {
return Arrays.asList(
new Pojo("123", 100),
new Pojo("123", 100),
new Pojo("123", 100),
new Pojo("456", 200)
);
}
public static void main(String[] args) {
System.out.println(getList().stream()
// extract a key for each Pojo in here.
// concatenating name and price together works as an example
.filter(distinctByKey(p -> p.getName() + p.getPrice()))
.collect(Collectors.toList()));
}
}
class Pojo {
private final String name;
private final Integer price;
public Pojo(final String name, final Integer price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public Integer getPrice() {
return price;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Pojo{");
sb.append("name='").append(name).append('\'');
sb.append(", price=").append(price);
sb.append('}');
return sb.toString();
}
}
This main method yields:
这个主要方法产生:
[Pojo{name='123', price=100}, Pojo{name='456', price=200}]
[Pojo{name='123', price=100}, Pojo{name='456', price=200}]
Edit
编辑
Made price an int
per Eugene's prompting.
int
根据尤金的提示定价。
Note: that you could use something more interesting as a key if you wanted to flesh it out:
注意:如果你想充实它,你可以使用更有趣的东西作为关键:
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;
class Class {
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 List<Pojo> getList() {
return Arrays.asList(
new Pojo("123", 100),
new Pojo("123", 100),
new Pojo("123", 100),
new Pojo("456", 200)
);
}
private static class NameAndPricePojoKey {
final String name;
final int price;
public NameAndPricePojoKey(final Pojo pojo) {
this.name = pojo.getName();
this.price = pojo.getPrice();
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final NameAndPricePojoKey that = (NameAndPricePojoKey) o;
if (price != that.price) return false;
return name != null ? name.equals(that.name) : that.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + price;
return result;
}
}
public static void main(String[] args) {
System.out.println(getList().stream()
// extract a key for each Pojo in here.
.filter(distinctByKey(NameAndPricePojoKey::new))
.collect(Collectors.toList()));
}
}
class Pojo {
private String name;
private Integer price;
private Object otherField1;
private Object otherField2;
public Pojo(final String name, final Integer price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(final String name) {
this.name = name;
}
public Integer getPrice() {
return price;
}
public void setPrice(final Integer price) {
this.price = price;
}
public Object getOtherField1() {
return otherField1;
}
public void setOtherField1(final Object otherField1) {
this.otherField1 = otherField1;
}
public Object getOtherField2() {
return otherField2;
}
public void setOtherField2(final Object otherField2) {
this.otherField2 = otherField2;
}
@Override
public boolean equals(final Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
final Pojo pojo = (Pojo) o;
if (name != null ? !name.equals(pojo.name) : pojo.name != null) return false;
if (price != null ? !price.equals(pojo.price) : pojo.price != null) return false;
if (otherField1 != null ? !otherField1.equals(pojo.otherField1) : pojo.otherField1 != null) return false;
return otherField2 != null ? otherField2.equals(pojo.otherField2) : pojo.otherField2 == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + (price != null ? price.hashCode() : 0);
result = 31 * result + (otherField1 != null ? otherField1.hashCode() : 0);
result = 31 * result + (otherField2 != null ? otherField2.hashCode() : 0);
return result;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Pojo{");
sb.append("name='").append(name).append('\'');
sb.append(", price=").append(price);
sb.append(", otherField1=").append(otherField1);
sb.append(", otherField2=").append(otherField2);
sb.append('}');
return sb.toString();
}
}
回答by Harmlezz
Here is my solution based on the class Item
which defines a name
and a price
:
这是我基于Item
定义 aname
和 a的类的解决方案price
:
public class Item {
public String name;
public double price;
Item(String name, double price) {
this.name = name;
this.price = price;
}
}
The requirement is to obtain only Item
s from a given List<Item>
which have distinct name
s anddistinct price
s, in the order in which they occur.
要求是Item
从给定的 s 中仅获得s,List<Item>
这些name
s具有不同的s和不同的price
s,按照它们出现的顺序。
I catch this requirement of being distinct by a class ItemWrapper
:
我抓住了这个由类区分的要求ItemWrapper
:
public class ItemWrapper {
Item item;
ItemWrapper(Item item) {
this.item = item;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ItemWrapper)) return false;
ItemWrapper other = (ItemWrapper) obj;
return Objects.equals(item.name, other.item.name) ||
item.price == other.item.price;
}
@Override
public int hashCode() {
return 1;
}
}
Now we have everything in place to filter a given List<Item>
of items:
现在我们已经准备好过滤给定List<Item>
的项目:
List<Item> items = Arrays.asList(
new Item("name-1", 100.00),
new Item("name-1", 555.00),
new Item("name-2", 100.00),
new Item("name-2", 999.99),
new Item("name-3", 100.00),
new Item("name-4", 555.00),
new Item("name-5", 999.99)
);
as following:
如下:
items.stream()
.map(item -> new ItemWrapper(item))
.distinct()
.map(wrapper -> wrapper.item)
.collect(Collectors.toList());
}
The items captured are:
捕获的项目是:
- name=name-1, price=100.0
- name=name-2, price=999.99
- name=name-4, price=555.0
- 名称=名称-1,价格=100.0
- 名称=名称-2,价格=999.99
- 名称=名称-4,价格=555.0
回答by lukens
I'd go for something like this, which is fairly simple and flexible, and builds on your example:
我会做这样的事情,它相当简单和灵活,并以您的示例为基础:
public static <T> List<T> distinctList(List<T> list, Function<? super T, ?>... keyExtractors) {
return list
.stream()
.filter(distinctByKeys(keyExtractors))
.collect(Collectors.toList());
}
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;
};
}
This can then be called in the following manner:
然后可以通过以下方式调用它:
final List<Xyz> distinct = distinctList(list, Xyz::getName, Xyz::getPrice)