Java Stream: foydalanuvchi yozuvlarining oxirgi versiyasini oling

Quyidagi tarzda aniqlangan foydalanuvchi moslamalari ro'yxati mavjud:

public class User {
    private String userId;//Unique identifier
    private String name;
    private String surname;
    private String otherPersonalInfo;
    private int versionNumber;
    }
    public User(String userId, String name, String surname, String otherPersonalInfo, int version) {
      super();
      this.name = name;
      this.surname = surname;
      this.otherPersonalInfo = otherPersonalInfo;
      this.version = version;
    }
}

Namunalar ro'yxati:

List users = Arrays.asList(
  new User("JOHNSMITH", "John", "Smith", "Some info",     1),
  new User("JOHNSMITH", "John", "Smith", "Updated info",  2),
  new User("JOHNSMITH", "John", "Smith", "Latest info",   3),
  new User("BOBDOE",    "Bob",  "Doe",   "Personal info", 1),
  new User("BOBDOE",    "Bob",  "Doe",   "Latest info",   2)
);

Ushbu ro'yxatni filtrlashning bir yo'li kerak, shuning uchun har bir foydalanuvchi uchun faqat oxirgi versiyani olaman, ya'ni:

{"JOHNSMITH", "John", "Smith", "Latest info", 3},
{"BOBDOE", "Bob", "Doe", "Latest info", 2}

Bunga erishishning eng yaxshi usuli - Java8 Stream API-ni ishlatishmi?

3
Ro'yxatni versiya bo'yicha tartiblagan Comparator dan foydalangan holda harakat qildingizmi?
qo'shib qo'ydi muallif Anand Undavia, manba

6 javoblar

bu javobdan ozroq yordam bilan:

    Collection latestVersions = users.stream()
            .collect(Collectors.groupingBy(User::getUserId,
                    Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(User::getVersionNumber)), Optional::get)))
                    .values();

Men odatdagilarni qabul qilaman. Natija:

[John Smith Latest info 3, Bob Doe Latest info 2]
6
qo'shib qo'ydi
@Nik Melis: yaxshi, istisnosiz xabar allaqachon aytadi. null kalitlariga ruxsat berilmaydi, shuning uchun fullName , resp. userId xususiyati null bo'lmasligi kerak.
qo'shib qo'ydi muallif Holger, manba
To'liq ismni bu yerda o'zgartirish uchun noyob userId qo'shdik. Xuddi shu kontseptsiya bo'lsa ham.
qo'shib qo'ydi muallif Nick Melis, manba
Agar kodni JUnit testida ishlatish menga xato bo'lsa ham beradi: java.lang.NullPointerException: element null kalit bilan eşlenemez
qo'shib qo'ydi muallif Nick Melis, manba
Xolger Ha, bu faqat meni laqillaganimga uzr so'rayman :)
qo'shib qo'ydi muallif Nick Melis, manba

Ro'yxatda birinchi bo'lib yangi ro'yxatga olishni ta'minlash uchun birinchi bo'lib versiya bo'yicha tartiblanganman. Keyinchalik, bu kalitga mos keladigan bitta ob'ektni natija qismining bir qismi bo'lishini ta'minlash uchun alohida kalitga sukut qildim. Filtrni qilish uchun men oldindan ko'rilgan narsalarni filtrlash uchun davlatni saqlaydigan predicate kerak edi.

Bevosita quyidagicha ko'rinadi:

    private static  Predicate distinctByKey( Function<? super T, ?> key ) {
    Map seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent( key.apply( t ), Boolean.TRUE ) == null;
}

Va keyin quyidagi oqimdan foydalanishim mumkin:

users.stream().sorted( ( u1, u2 ) -> u2.versionNumber - u1.versionNumber )
              .filter( distinctByKey( u -> u.name + u.surname ) )
              .collect( Collectors.toList() );

Java 8-dan farqli bo'lgan kalitda alohida bazani yaratish uchun boshqa yaxshi echimlar mavjud. mulk bo'yicha .

1
qo'shib qo'ydi
HashMap map = users.stream().collect(Collectors.toMap(User::getUserId, 
            e -> e, 
            (left, right) -> {return left.getVersion() > right.getVersion() ? left : right;}, 
            HashMap::new));
System.out.println(map.values());

Yuqoridagi kodning nusxalari:

[User [userId=BOBDOE, name=Bob, surname=Doe, otherPersonalInfo=Latest info, version=2], User [userId=JOHNSMITH, name=John, surname=Smith, otherPersonalInfo=Latest info, version=3]]

Izoh: toMap usuli 4 ta argument oladi:

  1. keyMapper a mapping function to produce keys
  2. valueMapper a mapping function to produce values
  3. mergeFunction a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction)
  4. mapSupplier a function which returns a new, empty Map into which the results will be inserted

  1. Birinchi arg argumentini olish uchun User :: getUserId() dir.
  2. Ikkinchi arg argumenti User ob'ektini bo'lgani kabi qaytaradi.
  3. Uchinchi arg argumenti foydalanuvchini eng so'nggi versiyasi bilan taqqoslash va saqlash orqali to'qnashuvni hal qiladi.
  4. To'rtinchi arg "HashMap" ning "yangi" uslubi.
1
qo'shib qo'ydi

java 8 da lambda ifodasi shaklida taqqoslash qurilmasi yaratishingiz mumkin.

taqqoslashda o'tgan users.stream (). tartibida ga qo'ng'iroq qiling.

Misol:

 Comparator byVersionNumber = (u1, u2) -> Integer.compare(
            u1.getversionNumber(), u2.getversionNumber());

    users.stream().sorted(byVersionNumber)
            .forEach(u -> System.out.println(u));

Iltimos, sintaksisning qo'polligini tekshiring

0
qo'shib qo'ydi

Bu og'riqli bo'ladi, lekin Java 8 Streams ramkasida ba'zi yig'ilishlar bilan amalga oshirilishi mumkin:

// create a Map from user name to users, sorted by version
Map> grouped =
        users.stream()
             .collect(
                     Collectors.groupingBy(
                             u -> u.name + "," + u.surname,
                             HashMap::new,
                             Collectors.toCollection(
                                    () -> new TreeSet<>(
                                             Comparator.comparing(
                                                   User::getVersionNumber)))));

// retrieve the latest versions from the Map
List latestVersions = grouped.entrySet()
                                   .stream()
                                   .map(e -> e.getValue().last())
                                   .collect(Collectors.toList());

Bu qanchalik aniq bo'lsa-da, ehtimol, men majburiy hal qilishga qaror qilardim.

  • Keep a Map
  • For every User, check whether the Map already contains the User's string representation
  • If it doesn't, or the User mapped to it has a lower version number, store the User in the Map.
0
qo'shib qo'ydi
Kollektor o'rniga Collector.toCollection (() -> new TreeSet <> (Comparator.comparing (User :: getVersionNumber)) dan foydalanishingiz mumkin. yangi TreeSet <> (Comparator.comparing (User :: getVersionNumber)), to'siq :: qo'shish (chap, o'ng) -> {left.addAll (o'ng); chapga qaytib;})
qo'shib qo'ydi muallif Holger, manba
@NickMelis ha, albatta. Bu noto'g'ri pozitsiyani yo'qotadi (Jon Smit ishi)
qo'shib qo'ydi muallif Sean Patrick Floyd, manba
@Holger yaxshi, rahmat.
qo'shib qo'ydi muallif Sean Patrick Floyd, manba
Agar foydalanuvchi sinfida identifikator maydoni bo'lsa (unda ismingiz + familiyangizni guruhlarga ajratishdan ko'ra) foydali bo'ladimi?
qo'shib qo'ydi muallif Nick Melis, manba
 List users = Arrays.asList(
                new User("JOHNSMITH", "John", "Smith", "Some info", 1),
                new User("JOHNSMITH", "John", "Smith", "Updated info", 2),
                new User("JOHNSMITH", "John", "Smith", "Latest info", 3),
                new User("BOBDOE", "Bob", "Doe", "Personal info", 1),
                new User("BOBDOE", "Bob", "Doe", "Latest info", 2)
        ).stream()
                .collect(Collectors.collectingAndThen(
                        Collectors.toMap(
                                User::getUserId,     //The user's unique property
                                Function.identity(), //Function
                                BinaryOperator.maxBy(Comparator.comparing(User::getVersionNumber))

                        ),
                        map -> (List)map.values() 
                ));
0
qo'shib qo'ydi