Java 8 Phân biệt theo thuộc tính


455

Trong Java 8, làm cách nào tôi có thể lọc một bộ sưu tập bằng StreamAPI bằng cách kiểm tra tính khác biệt của một thuộc tính của từng đối tượng?

Ví dụ: tôi có một danh sách các Personđối tượng và tôi muốn xóa những người có cùng tên,

persons.stream().distinct();

Sẽ sử dụng kiểm tra đẳng thức mặc định cho một Personđối tượng, vì vậy tôi cần một cái gì đó như,

persons.stream().distinct(p -> p.getName());

Thật không may distinct()phương pháp không có quá tải như vậy. Nếu không sửa đổi kiểm tra bình đẳng trong Personlớp thì có thể làm điều này ngắn gọn không?

Câu trả lời:


556

Xem xét distinctlà một bộ lọc trạng thái . Đây là một hàm trả về một vị từ duy trì trạng thái về những gì nó đã thấy trước đó và trả về lần đầu tiên phần tử đã cho có được nhìn thấy hay không:

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Set<Object> seen = ConcurrentHashMap.newKeySet();
    return t -> seen.add(keyExtractor.apply(t));
}

Sau đó, bạn có thể viết:

persons.stream().filter(distinctByKey(Person::getName))

Lưu ý rằng nếu luồng được sắp xếp và chạy song song, điều này sẽ bảo toàn một phần tử tùy ý trong số các bản sao, thay vì phần đầu tiên, như distinct()vậy.

(Điều này về cơ bản giống như câu trả lời của tôi cho câu hỏi này: Java Lambda Stream Distinc () trên khóa tùy ý? )


27
Tôi đoán để tương thích tốt hơn các đối số nên được Function<? super T, ?>, không Function<? super T, Object>. Ngoài ra, cần lưu ý rằng đối với luồng song song có trật tự, giải pháp này không đảm bảo đối tượng nào sẽ được trích xuất (không giống như bình thường distinct()). Ngoài ra, đối với các luồng liên tiếp có thêm chi phí sử dụng CHM (không có trong giải pháp @nosid). Cuối cùng, giải pháp này vi phạm hợp đồng của filterphương thức mà vị ngữ phải không trạng thái như đã nêu trong JavaDoc. Tuy nhiên được nâng cấp.
Tagir Valeev

3
@java_newbie Ví dụ Vị ngữ được trả về distinctByKeykhông biết liệu nó có được sử dụng trong luồng song song không. Nó sử dụng CHM trong trường hợp nó đang được sử dụng song song, mặc dù điều này bổ sung thêm chi phí trong trường hợp tuần tự như Tagir Valeev đã lưu ý ở trên.
Stuart Marks

5
@holandaGo Sẽ thất bại nếu bạn lưu và sử dụng lại đối tượng Vị ngữ được trả về bởi distinctByKey. Nhưng nó hoạt động nếu bạn gọi distinctByKeymỗi lần, để nó tạo ra một cá thể Vị ngữ mới mỗi lần.
Stuart Marks

3
@Chinmay không, không nên. Nếu bạn sử dụng .filter(distinctByKey(...)). Nó sẽ thực thi phương thức một lần và trả về vị ngữ. Vì vậy, về cơ bản bản đồ đã được sử dụng lại nếu bạn sử dụng đúng cách trong luồng. Nếu bạn làm cho bản đồ tĩnh, bản đồ sẽ được chia sẻ cho tất cả các mục đích sử dụng. Vì vậy, nếu bạn có hai luồng sử dụng điều này distinctByKey(), cả hai sẽ sử dụng cùng một bản đồ, đó không phải là điều bạn muốn.
g00glen00b

3
Điều này thật thông minh và hoàn toàn không rõ ràng. Nói chung, đây là một lambda có trạng thái và cơ sở CallSitesẽ được liên kết với get$Lambdaphương thức - nó sẽ trả về một thể hiện mới Predicatemọi lúc, nhưng những trường hợp đó sẽ chia sẻ tương tự mapfunctiontheo như tôi hiểu. Rất đẹp!
Eugene

152

Một cách khác là đặt những người trong bản đồ bằng cách sử dụng tên làm khóa:

persons.collect(Collectors.toMap(Person::getName, p -> p, (p, q) -> p)).values();

Lưu ý rằng Người được giữ, trong trường hợp có tên trùng lặp, sẽ là người đầu tiên được mã hóa.


23
@skiwi: bạn có nghĩ rằng có một cách để thực hiện distinct()mà không cần chi phí đó không? Làm thế nào bất kỳ triển khai sẽ biết nếu nó đã nhìn thấy một đối tượng trước đây mà không thực sự nhớ tất cả các giá trị riêng biệt mà nó đã thấy? Vì vậy, chi phí chung toMapdistinctrất có thể giống nhau.
Holger

1
@Holger Tôi có thể đã sai ở đó vì tôi đã không nghĩ rằng abou distinct()chính nó tạo ra.
skiwi

2
Và rõ ràng là nó làm xáo trộn thứ tự ban đầu của danh sách
Philipp

10
@Philipp: có thể được sửa bằng cách đổi thànhpersons.collect(toMap(Person::getName, p -> p, (p, q) -> p, LinkedHashMap::new)).values();
Holger

1
@DanielEarwicker câu hỏi này là về "phân biệt theo tài sản". Nó sẽ yêu cầu luồng được sắp xếp theo cùng một thuộc tính , để có thể tận dụng lợi thế của nó. Đầu tiên, OP không bao giờ nói rằng luồng được sắp xếp cả. Thứ hai, các luồng không thể phát hiện xem chúng có được sắp xếp theo một thuộc tính nhất định hay không . Thứ ba, không có hoạt động luồng "phân biệt theo tài sản" chính hãng để làm những gì bạn đề xuất. Trong thực tế, chỉ có hai cách để có được một luồng được sắp xếp như vậy. Một nguồn được sắp xếp ( TreeSet) dù sao cũng đã khác biệt hoặc sortedtrên luồng cũng đệm tất cả các phần tử.
Holger

101

Bạn có thể bọc các đối tượng người vào một lớp khác, chỉ so sánh tên của những người đó. Sau đó, bạn mở khóa các đối tượng được bọc để có được một luồng người một lần nữa. Các hoạt động luồng có thể trông như sau:

persons.stream()
    .map(Wrapper::new)
    .distinct()
    .map(Wrapper::unwrap)
    ...;

Lớp học Wrappercó thể trông như sau:

class Wrapper {
    private final Person person;
    public Wrapper(Person person) {
        this.person = person;
    }
    public Person unwrap() {
        return person;
    }
    public boolean equals(Object other) {
        if (other instanceof Wrapper) {
            return ((Wrapper) other).person.getName().equals(person.getName());
        } else {
            return false;
        }
    }
    public int hashCode() {
        return person.getName().hashCode();
    }
}

13
Đây được gọi là biến đổi Schwartzian
Stuart Caie

5
@StuartCaie Không thực sự ... không có ghi nhớ và vấn đề không phải là hiệu suất, mà là sự thích ứng với API hiện có.
Marko Topolnik

6
com.google.common.base.Equivalence.wrap (S) và com.google.common.base.Equivalence.Wrapper.get () cũng có thể giúp đỡ.
bjmi

Bạn có thể làm cho lớp bao bọc chung chung và được tham số hóa bằng một hàm trích xuất khóa.
Lii

Các equalsphương pháp có thể được đơn giản hóa đếnreturn other instanceof Wrapper && ((Wrapper) other).person.getName().equals(person.getName());
Holger

55

Một giải pháp khác, sử dụng Set. Có thể không phải là giải pháp lý tưởng, nhưng nó hoạt động

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

Hoặc nếu bạn có thể sửa đổi danh sách gốc, bạn có thể sử dụng phương thức remove If

persons.removeIf(p -> !set.add(p.getName()));

2
Đây là câu trả lời tốt nhất nếu bạn không sử dụng bất kỳ thư viện bên thứ ba nào!
Manoj Shrestha

5
sử dụng ý tưởng tuyệt vời mà Set.add trả về đúng nếu bộ này chưa chứa phần tử được chỉ định. +1
Luvie

Tôi tin rằng phương pháp này không hoạt động để xử lý luồng song song, vì nó không an toàn cho luồng.
LoBo

@LoBo Có lẽ là không. Đây chỉ là một ý tưởng, sẽ làm việc cho các trường hợp đơn giản. Người dùng có thể mở rộng nó để an toàn / song song luồng.
Santhosh

Cách tiếp cận thú vị, nhưng trông giống như một mô hình chống để sửa đổi (bộ) bên ngoài trong khi lọc luồng trên bộ sưu tập khác (người) ...
Justin Rowe

31

Có một cách tiếp cận đơn giản hơn bằng cách sử dụng TreeSet với bộ so sánh tùy chỉnh.

persons.stream()
    .collect(Collectors.toCollection(
      () -> new TreeSet<Person>((p1, p2) -> p1.getName().compareTo(p2.getName())) 
));

4
Tôi nghĩ rằng câu trả lời của bạn giúp hướng tới thứ tự và không hướng tới sự độc đáo. Tuy nhiên nó đã giúp tôi đặt suy nghĩ của mình về cách làm điều đó. Kiểm tra tại đây: stackoverflow.com/questions/1019854/ Lời
janagn 14/03/2015

Hãy nhớ rằng bạn sẽ trả giá cho việc sắp xếp các yếu tố ở đây và chúng tôi không cần sắp xếp để tìm các bản sao hoặc thậm chí loại bỏ các bản sao.
pisaruk

12
So sánh.compared (Person :: getName)
Jean-François Savard

24

Chúng tôi cũng có thể sử dụng RxJava ( thư viện mở rộng phản ứng rất mạnh )

Observable.from(persons).distinct(Person::getName)

hoặc là

Observable.from(persons).distinct(p -> p.getName())

Rx là tuyệt vời, nhưng đây là một câu trả lời kém. Observablelà dựa trên đẩy trong khi đó Streamlà dựa trên kéo. stackoverflow.com/questions/30216979/ từ
sdgfsdh

4
câu hỏi yêu cầu một giải pháp java8 không nhất thiết phải sử dụng luồng. Câu trả lời của tôi cho thấy api stream java8 ít powefull hơn rx api
frhack

1
Sử dụng lò phản ứng , nó sẽ làFlux.fromIterable(persons).distinct(p -> p.getName())
Ritesh

Câu hỏi có nghĩa đen là "sử dụng StreamAPI", không phải "không nhất thiết phải sử dụng luồng". Điều đó nói rằng, đây là một giải pháp tuyệt vời cho vấn đề XY trong việc lọc luồng thành các giá trị riêng biệt.
M. Justin

12

Bạn có thể sử dụng groupingBybộ sưu tập:

persons.collect(Collectors.groupingBy(p -> p.getName())).values().forEach(t -> System.out.println(t.get(0).getId()));

Nếu bạn muốn có một luồng khác, bạn có thể sử dụng luồng này:

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream().map(l -> (l.get(0)));

11

Bạn có thể sử dụng distinct(HashingStrategy)phương thức trong Bộ sưu tập Eclipse .

List<Person> persons = ...;
MutableList<Person> distinct =
    ListIterate.distinct(persons, HashingStrategies.fromFunction(Person::getName));

Nếu bạn có thể cấu trúc lại personsđể triển khai giao diện Bộ sưu tập Eclipse, bạn có thể gọi phương thức trực tiếp trong danh sách.

MutableList<Person> persons = ...;
MutableList<Person> distinct =
    persons.distinct(HashingStrategies.fromFunction(Person::getName));

HashingStrargety chỉ đơn giản là một giao diện chiến lược cho phép bạn xác định các cài đặt tùy chỉnh của bằng và mã băm.

public interface HashingStrategy<E>
{
    int computeHashCode(E object);
    boolean equals(E object1, E object2);
}

Lưu ý: Tôi là người đi làm cho Bộ sưu tập Eclipse.


Phương thức differBy đã được thêm vào trong Bộ sưu tập Eclipse 9.0 có thể đơn giản hóa hơn nữa giải pháp này. Medium.com/@donraab/
Donald Raab

10

Tôi khuyên bạn nên sử dụng Vavr , nếu bạn có thể. Với thư viện này, bạn có thể làm như sau:

io.vavr.collection.List.ofAll(persons)
                       .distinctBy(Person::getName)
                       .toJavaSet() // or any another Java 8 Collection

Trước đây được gọi là thư viện "javaslang".
dùng11153

9

Bạn có thể sử dụng thư viện StreamEx :

StreamEx.of(persons)
        .distinct(Person::getName)
        .toList()

Thật không may, phương pháp đó của thư viện StreamEx tuyệt vời khác được thiết kế kém - nó so sánh sự bình đẳng của đối tượng thay vì sử dụng bằng. Điều này có thể làm việc cho Strings nhờ vào thực tập chuỗi, nhưng nó cũng có thể không.
Mô-men xoắn

7

Mở rộng câu trả lời của Stuart Marks, điều này có thể được thực hiện theo cách ngắn hơn và không có bản đồ đồng thời (nếu bạn không cần các luồng song song):

public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    final Set<Object> seen = new HashSet<>();
    return t -> seen.add(keyExtractor.apply(t));
}

Sau đó gọi:

persons.stream().filter(distinctByKey(p -> p.getName());

2
Điều này không xem xét rằng luồng có thể song song.
brunnsbe

Cảm ơn vì nhận xét, tôi đã cập nhật câu trả lời của mình. Nếu bạn không cần một luồng song song, việc không sử dụng bản đồ đồng thời mang lại cho bạn hiệu suất tốt hơn nhiều.
Wojciech Górski

Mã của bạn có thể sẽ hoạt động cho các bộ sưu tập song song nếu bạn tạo một Collections.synchronizedSet(new HashSet<>())thay thế. Nhưng nó có lẽ sẽ chậm hơn với a ConcurrentHashMap.
Lii

7

Cách tiếp cận tương tự mà Saeed Zarinfam đã sử dụng nhưng kiểu Java 8 hơn :)

persons.collect(Collectors.groupingBy(p -> p.getName())).values().stream()
 .map(plans -> plans.stream().findFirst().get())
 .collect(toList());

1
Tôi sẽ thay thế dòng bản đồ bằng flatMap(plans -> plans.stream().findFirst().stream())nó để tránh sử dụng tùy chọn
Andrew Sneck

Có lẽ điều này cũng ổn: FlatMap (kế hoạch -> kế hoạch. Dòng (). Giới hạn (1))
Rrr

6

Tôi đã tạo một phiên bản chung:

private <T, R> Collector<T, ?, Stream<T>> distinctByKey(Function<T, R> keyExtractor) {
    return Collectors.collectingAndThen(
            toMap(
                    keyExtractor,
                    t -> t,
                    (t1, t2) -> t1
            ),
            (Map<R, T> map) -> map.values().stream()
    );
}

Một ví dụ:

Stream.of(new Person("Jean"), 
          new Person("Jean"),
          new Person("Paul")
)
    .filter(...)
    .collect(distinctByKey(Person::getName)) // return a stream of Person with 2 elements, jean and Paul
    .map(...)
    .collect(toList())



5

Cách tiếp cận của tôi với điều này là nhóm tất cả các đối tượng có cùng thuộc tính với nhau, sau đó cắt ngắn các nhóm thành kích thước 1 và cuối cùng thu thập chúng dưới dạng a List.

  List<YourPersonClass> listWithDistinctPersons =   persons.stream()
            //operators to remove duplicates based on person name
            .collect(Collectors.groupingBy(p -> p.getName()))
            .values()
            .stream()
            //cut short the groups to size of 1
            .flatMap(group -> group.stream().limit(1))
            //collect distinct users as list
            .collect(Collectors.toList());

3

Danh sách các đối tượng khác biệt có thể được tìm thấy bằng cách sử dụng:

 List distinctPersons = persons.stream()
                    .collect(Collectors.collectingAndThen(
                            Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Person:: getName))),
                            ArrayList::new));

2

Cách dễ nhất để thực hiện điều này là nhảy vào tính năng sắp xếp vì nó đã cung cấp một tùy chọn Comparatorcó thể được tạo bằng thuộc tính của một phần tử. Sau đó, bạn phải lọc các bản sao có thể được thực hiện bằng cách sử dụng trạng thái Predicatesử dụng thực tế là đối với luồng được sắp xếp, tất cả các phần tử bằng nhau đều liền kề:

Comparator<Person> c=Comparator.comparing(Person::getName);
stream.sorted(c).filter(new Predicate<Person>() {
    Person previous;
    public boolean test(Person p) {
      if(previous!=null && c.compare(previous, p)==0)
        return false;
      previous=p;
      return true;
    }
})./* more stream operations here */;

Tất nhiên, một trạng thái Predicatekhông an toàn cho luồng, tuy nhiên nếu đó là nhu cầu của bạn, bạn có thể chuyển logic này sang Collectorvà để luồng xử lý an toàn luồng khi sử dụng Collector. Điều này phụ thuộc vào những gì bạn muốn làm với dòng các yếu tố riêng biệt mà bạn đã không nói với chúng tôi trong câu hỏi của bạn.


1

Dựa trên câu trả lời của @ josketres, tôi đã tạo ra một phương thức tiện ích chung:

Bạn có thể làm cho Java 8 thân thiện hơn bằng cách tạo Collector .

public static <T> Set<T> removeDuplicates(Collection<T> input, Comparator<T> comparer) {
    return input.stream()
            .collect(toCollection(() -> new TreeSet<>(comparer)));
}


@Test
public void removeDuplicatesWithDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(7), new C(42), new C(42));
    Collection<C> result = removeDuplicates(input, (c1, c2) -> Integer.compare(c1.value, c2.value));
    assertEquals(2, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 7));
    assertTrue(result.stream().anyMatch(c -> c.value == 42));
}

@Test
public void removeDuplicatesWithoutDuplicates() {
    ArrayList<C> input = new ArrayList<>();
    Collections.addAll(input, new C(1), new C(2), new C(3));
    Collection<C> result = removeDuplicates(input, (t1, t2) -> Integer.compare(t1.value, t2.value));
    assertEquals(3, result.size());
    assertTrue(result.stream().anyMatch(c -> c.value == 1));
    assertTrue(result.stream().anyMatch(c -> c.value == 2));
    assertTrue(result.stream().anyMatch(c -> c.value == 3));
}

private class C {
    public final int value;

    private C(int value) {
        this.value = value;
    }
}

1

Có lẽ sẽ hữu ích cho ai đó. Tôi đã có một chút yêu cầu khác. Có danh sách các đối tượng Atừ bên thứ 3 loại bỏ tất cả những gì có cùng một A.btrường cho cùng một danh sách A.id(nhiều Ađối tượng có cùng A.iddanh sách). Luồng trả lời phân vùng của Tagir Valeev đã truyền cảm hứng cho tôi sử dụng tùy chỉnh Collectortrả về Map<A.id, List<A>>. Đơn giản flatMapsẽ làm phần còn lại.

 public static <T, K, K2> Collector<T, ?, Map<K, List<T>>> groupingDistinctBy(Function<T, K> keyFunction, Function<T, K2> distinctFunction) {
    return groupingBy(keyFunction, Collector.of((Supplier<Map<K2, T>>) HashMap::new,
            (map, error) -> map.putIfAbsent(distinctFunction.apply(error), error),
            (left, right) -> {
                left.putAll(right);
                return left;
            }, map -> new ArrayList<>(map.values()),
            Collector.Characteristics.UNORDERED)); }

1

Tôi đã có một tình huống, trong đó tôi đã giả sử để có được các yếu tố khác biệt từ danh sách dựa trên 2 khóa. Nếu bạn muốn phân biệt dựa trên hai khóa hoặc có thể là phím tổng hợp, hãy thử điều này

class Person{
    int rollno;
    String name;
}
List<Person> personList;


Function<Person, List<Object>> compositeKey = personList->
        Arrays.<Object>asList(personList.getName(), personList.getRollno());

Map<Object, List<Person>> map = personList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));

List<Object> duplicateEntrys = map.entrySet().stream()`enter code here`
        .filter(settingMap ->
                settingMap.getValue().size() > 1)
        .collect(Collectors.toList());

0

Trong trường hợp của tôi, tôi cần kiểm soát phần tử trước đó là gì. Sau đó, tôi đã tạo một Vị ngữ có trạng thái nơi tôi kiểm soát nếu phần tử trước đó khác với phần tử hiện tại, trong trường hợp đó tôi giữ nó.

public List<Log> fetchLogById(Long id) {
    return this.findLogById(id).stream()
        .filter(new LogPredicate())
        .collect(Collectors.toList());
}

public class LogPredicate implements Predicate<Log> {

    private Log previous;

    public boolean test(Log atual) {
        boolean isDifferent = previouws == null || verifyIfDifferentLog(current, previous);

        if (isDifferent) {
            previous = current;
        }
        return isDifferent;
    }

    private boolean verifyIfDifferentLog(Log current, Log previous) {
        return !current.getId().equals(previous.getId());
    }

}

0

Giải pháp của tôi trong danh sách này:

List<HolderEntry> result ....

List<HolderEntry> dto3s = new ArrayList<>(result.stream().collect(toMap(
            HolderEntry::getId,
            holder -> holder,  //or Function.identity() if you want
            (holder1, holder2) -> holder1 
    )).values());

Trong tình huống của tôi, tôi muốn tìm các giá trị riêng biệt và đưa chúng vào Danh sách.


0

Mặc dù câu trả lời được đánh giá cao nhất là câu trả lời hoàn toàn tốt nhất cho Java 8, nhưng đồng thời nó lại hoàn toàn kém nhất về hiệu năng. Nếu bạn thực sự muốn một ứng dụng hiệu suất thấp, thì hãy tiếp tục và sử dụng nó. Yêu cầu đơn giản để trích xuất một nhóm Tên người duy nhất sẽ đạt được chỉ bằng "Dành cho mỗi" và "Tập hợp". Mọi thứ thậm chí còn tồi tệ hơn nếu danh sách có kích thước trên 10.

Hãy xem xét bạn có một bộ sưu tập gồm 20 Đối tượng, như thế này:

public static final List<SimpleEvent> testList = Arrays.asList(
            new SimpleEvent("Tom"), new SimpleEvent("Dick"),new SimpleEvent("Harry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Huckle"),new SimpleEvent("Berry"),new SimpleEvent("Tom"),
            new SimpleEvent("Dick"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("Cherry"),
            new SimpleEvent("Roses"),new SimpleEvent("Moses"),new SimpleEvent("Chiku"),new SimpleEvent("gotya"),
            new SimpleEvent("Gotye"),new SimpleEvent("Nibble"),new SimpleEvent("Berry"),new SimpleEvent("Jibble"));

Nơi bạn phản đối SimpleEventtrông như thế này:

public class SimpleEvent {

private String name;
private String type;

public SimpleEvent(String name) {
    this.name = name;
    this.type = "type_"+name;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}
}

Và để kiểm tra, bạn có mã JMH như thế này, (Xin lưu ý, tôi đang sử dụng cùng một Dự báo riêng biệt được đề cập trong câu trả lời được chấp nhận):

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aStreamBasedUniqueSet(Blackhole blackhole) throws Exception{

    Set<String> uniqueNames = testList
            .stream()
            .filter(distinctByKey(SimpleEvent::getName))
            .map(SimpleEvent::getName)
            .collect(Collectors.toSet());
    blackhole.consume(uniqueNames);
}

@Benchmark
@OutputTimeUnit(TimeUnit.SECONDS)
public void aForEachBasedUniqueSet(Blackhole blackhole) throws Exception{
    Set<String> uniqueNames = new HashSet<>();

    for (SimpleEvent event : testList) {
        uniqueNames.add(event.getName());
    }
    blackhole.consume(uniqueNames);
}

public static void main(String[] args) throws RunnerException {
    Options opt = new OptionsBuilder()
            .include(MyBenchmark.class.getSimpleName())
            .forks(1)
            .mode(Mode.Throughput)
            .warmupBatchSize(3)
            .warmupIterations(3)
            .measurementIterations(3)
            .build();

    new Runner(opt).run();
}

Sau đó, bạn sẽ có kết quả Điểm chuẩn như thế này:

Benchmark                                  Mode  Samples        Score  Score error  Units
c.s.MyBenchmark.aForEachBasedUniqueSet    thrpt        3  2635199.952  1663320.718  ops/s
c.s.MyBenchmark.aStreamBasedUniqueSet     thrpt        3   729134.695   895825.697  ops/s

Và như bạn có thể thấy, For-Each đơn giản tốt hơn thông lượng gấp 3 lần và ít hơn về điểm số lỗi so với Java 8 Stream.

Cao hơn thông, tốt hơn hiệu suất


1
Cảm ơn, nhưng câu hỏi rất cụ thể trong ngữ cảnh của API Stream
RichK

Vâng, tôi đồng ý, tôi đã đề cập "Trong khi câu trả lời được đánh giá cao nhất là câu trả lời hoàn toàn tốt nhất khi viết Java 8". Một vấn đề có thể được giải quyết theo nhiều cách khác nhau và tôi đang cố gắng nhấn mạnh ở đây rằng vấn đề trong tay có thể được giải quyết một cách đơn giản, thay vì nguy hiểm với Luồng Java 8, trong đó nguy cơ là suy giảm hiệu năng. :)
Abhinav Ganguly

0
Here is the example
public class PayRoll {

    private int payRollId;
    private int id;
    private String name;
    private String dept;
    private int salary;


    public PayRoll(int payRollId, int id, String name, String dept, int salary) {
        super();
        this.payRollId = payRollId;
        this.id = id;
        this.name = name;
        this.dept = dept;
        this.salary = salary;
    }
} 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class Prac {
    public static void main(String[] args) {

        int salary=70000;
        PayRoll payRoll=new PayRoll(1311, 1, "A", "HR", salary);
        PayRoll payRoll2=new PayRoll(1411, 2    , "B", "Technical", salary);
        PayRoll payRoll3=new PayRoll(1511, 1, "C", "HR", salary);
        PayRoll payRoll4=new PayRoll(1611, 1, "D", "Technical", salary);
        PayRoll payRoll5=new PayRoll(711, 3,"E", "Technical", salary);
        PayRoll payRoll6=new PayRoll(1811, 3, "F", "Technical", salary);
        List<PayRoll>list=new ArrayList<PayRoll>();
        list.add(payRoll);
        list.add(payRoll2);
        list.add(payRoll3);
        list.add(payRoll4);
        list.add(payRoll5);
        list.add(payRoll6);


        Map<Object, Optional<PayRoll>> k = list.stream().collect(Collectors.groupingBy(p->p.getId()+"|"+p.getDept(),Collectors.maxBy(Comparator.comparingInt(PayRoll::getPayRollId))));


        k.entrySet().forEach(p->
        {
            if(p.getValue().isPresent())
            {
                System.out.println(p.getValue().get());
            }
        });



    }
}

Output:

PayRoll [payRollId=1611, id=1, name=D, dept=Technical, salary=70000]
PayRoll [payRollId=1811, id=3, name=F, dept=Technical, salary=70000]
PayRoll [payRollId=1411, id=2, name=B, dept=Technical, salary=70000]
PayRoll [payRollId=1511, id=1, name=C, dept=HR, salary=70000]

-2

Nếu bạn muốn Danh sách những người theo sau sẽ là cách đơn giản

Set<String> set = new HashSet<>(persons.size());
persons.stream().filter(p -> set.add(p.getName())).collect(Collectors.toList());

Ngoài ra, nếu bạn muốn tìm danh sách tên riêng biệt hoặc duy nhất , không phải Người , bạn cũng có thể sử dụng hai phương pháp sau.

Phương pháp 1: sử dụng distinct

persons.stream().map(x->x.getName()).distinct.collect(Collectors.toList());

Cách 2: sử dụng HashSet

Set<E> set = new HashSet<>();
set.addAll(person.stream().map(x->x.getName()).collect(Collectors.toList()));

2
Điều này tạo ra một danh sách các tên, không phải Persons.
Hulk

1
Điều này thật đúng với gì mà tôi đã tìm kiếm. Tôi cần một phương thức dòng đơn để loại bỏ trùng lặp trong khi chuyển đổi một bộ sưu tập sang một bộ khác. Cảm ơn.
Raj

-3

Mã đơn giản nhất bạn có thể viết:

    persons.stream().map(x-> x.getName()).distinct().collect(Collectors.toList());

12
Tuy nhiên, điều đó sẽ nhận được một danh sách tên riêng biệt, chứ không phải Người theo tên
RichK
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.