1. ホーム
  2. ジャワ

java 8 ラムダ式 リスト操作 グループ化、フィルタリング、合計、最多、ソート、重複排除

2022-02-26 01:55:14

java 8のラムダ式は、リスト操作のための便利なメソッドを提供しています。主に、グループ化、フィルタリング、合計、ソート、重み付け解除をカバーしています。これは、従来のリストの書き方に比べて、かなり少ないコードで済みます。

新しいエンティティクラスを作成する

package com.vvvtimes.vo;

import java.math;
import java.util;

public class User {

    private Long id;

    //name
    private String name;

    //age
    private int age;

    //job number
    private String jobNumber;

    //gender
    private String sex;

    //entry date
    private Date entryDate;

    //number of family members
    private BigDecimal familyMemberQuantity;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getJobNumber() {
        return jobNumber;
    }

    public void setJobNumber(String jobNumber) {
        this.jobNumber = jobNumber;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Date getEntryDate() {
        return entryDate;
    }

    public void setEntryDate(Date entryDate) {
        this.entryDate = entryDate;
    }

    public BigDecimal getFamilyMemberQuantity() {
        return familyMemberQuantity;
    }

    public void setFamilyMemberQuantity(BigDecimal familyMemberQuantity) {
        this.familyMemberQuantity = familyMemberQuantity;
    }
}

1. グループ分けについて

groupingByを使用すると、特定のフィールドをグループ化することができます。

        //group
        Map<String, List<User>> groupBySex = userList.stream().collect(Collectors.groupingBy(User::getSex));
        //Iterate through the groups
        for (Map.Entry<String, List<User>> entryUser : groupBySex.entrySet()) {
            String key = entryUser.getKey();
            List<User> entryUserList = entryUser.getValue();
        }

トップドアのグループ化にはキーヌルセキュリティの問題があり、フィルタリングまたはラッピングが必要です

オプションのレポートによるラッピング 使用中の値が存在しない

 //Grouping to add cumulative
                Map<Optional<Long>, List<SalesOrderMonthlyStatementVo>> groupMap = list.stream().collect(Collectors.groupingBy(x -> ; Optional.ofNullable(x.getReviewerGroupId())));
                // Iterate through the groups
                for (Map.Entry<Optional<Long>, List<SalesOrderMonthlyStatementVo>> entryGroup : groupMap.entrySet()) {
                    Optional<Long> key = entryGroup.getKey();
                    Long groupId = key.get();
                    List<SalesOrderMonthlyStatementVo> entryGroupList = entryGroup.getValue();
}

正しい使い方は、以下のように、カスタムgroupingBy_WithNullKeysを使用することです。

/* Like Collectors.groupingBy, but accepts null keys. */
    public static <T, A> Collector<T, ? , Map<A, List<T>>>
    groupingBy_WithNullKeys(Function<? super T, ? extends A> classifier) {
        return Collectors.toMap(
                classifier,
                Collections::singletonList,
                (List<T> oldList, List<T> newEl) -> {
                    List<T> newList = new ArrayList<>(oldList.size() + 1);
                    newList.addAll(oldList);
                    newList.addAll(newEl);
                    return newList;
                });
    }

次に

Map<Long, List<SalesOrderMonthlyStatementVo>> groupMap = list.stream().collect(groupingBy_WithNullKeys(x -> x. getReviewerGroupId()));


マルチフィールドグルーピング

        Function<WarehouseReceiptLineBatch, List<Object>> compositeKey = wlb ->
                Arrays.<Object>asList(wlb.getWarehouseReceiptLineId(), wlb.getWarehouseAreaId(), wlb.getWarehouseLocationId());
        Map<Object, List<WarehouseReceiptLineBatch>> map =
                warehouseReceiptLineBatchList.stream().collect(Collectors.groupingBy(compositeKey, Collectors.toList()));
        //Iterate through the groups
        for (Map.Entry<Object, List<WarehouseReceiptLineBatch>> entryUser : map.entrySet()) {
            List<Object> key = (List<Object>) entryUser.getKey();
            List<WarehouseReceiptLineBatch> entryUserList = entryUser.getValue();
            Long warehouseReceiptLineId = (Long) key.get(0);
            Long warehouseAreaId = (Long) key.get(1);
            Long warehouseLocationId = (Long) key.get(2);

        }


2. フィルタリング

filter メソッドを使用すると、特定の条件をフィルタリングすることができます

        //filter
        //Exclude users with work number 201901
        List<User> userCommonList = userList.stream().filter(a -> !a.getJobNumber().equals("201901")).collect(Collectors. toList());


3. 和算

基本型とラージ型で合計し、基本型はmapToIntしてsumメソッドを呼び、ラージ型はreduceしてBigDecimal::addメソッドを呼びます。

        //summing
        //basic type
        int sumAge = userList.stream().mapToInt(User::getAge).sum();
        //BigDecimal summation
        BigDecimal totalQuantity = userList.stream().map(User::getFamilyMemberQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);


上記のsumationでは、bigDecimalオブジェクトがNULLの場合をフィルタリングできず、NULLポインタを報告することがありますが、その場合は、filterメソッドでフィルタリングするか、sumationメソッドを書き換えます。

集計方法を書き換える

package com.vvvtimes.util;

import java.math;

public class BigDecimalUtils {

    public static BigDecimal ifNullSet0(BigDecimal in) {
        if (in ! = null) {
            return in;
        }
        return BigDecimal.ZERO;
    }

    public static BigDecimal sum(BigDecimal . .in){
        BigDecimal result = BigDecimal.ZERO;
        for (int i = 0; i < in.length; i++){
            result = result.add(ifNullSet0(in[i]));
        }
        return result;
    }
}


オーバーライト方式を使用

BigDecimal totalQuantity2 = userList.stream().map(User::getFamilyMemberQuantity).reduce(BigDecimal.ZERO, BigDecimalUtils::sum);


オブジェクトのNULL判定

stream.filter(x -> x!=null)
stream.filter(Objects::nonNull)

stream.filter(x -> x.getDateTime()! =null)

フィールドNULLの判定

        //min
        Date minEntryDate = userList.stream().map(User::getEntryDate).min(Date::compareTo).get();

        //max
        Date maxEntryDate = userList.stream().map(User::getEntryDate).max(Date::compareTo).get();

4. ベストバリュー

min max法でminとmaxを求める。

Comparator<LeasingBusinessContract> comparator = Comparator.comparing(LeasingBusinessContract::getLeaseEndDate);
LeasingBusinessContract maxObject = leasingBusinessContractList.stream().max(comparator).get();

時には、最大値と最小値に対応するオブジェクトを知る必要がありますが、これは次のようにして得ることができます。

         /**
         * List -> Map
         * One thing to note is.
         * toMap If the collection object has duplicate keys, it will report the error Duplicate key ....
         * user1,user2 id are 1.
         * You can use (k1,k2)-> k1 to set, if there are duplicate keys, then keep key1, discard key2
         */
        Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, a -> a,(k1,k2)->k1));


5. マップへのリスト

Map<String, WorkCenterLoadVo> workCenterMap = list.stream().collect(Collectors.toMap(key->DateFormatUtils.format(key.getDate( ), "yyyy-MM-dd"), a -> a,(k1,k2)->k1));

リストをマップに変換するとき、キーとして日付型を使うことがありますが、実際には文字列を使うことの方が多いので、フィールドを文字列に変換することができます

        //Sort
        //Sort by single field, sort by id
        userList.sort(Comparator.comparing(User::getId));
        //sort by multiple fields, sort by id, age
        userList.sort(Comparator.comparing(User::getId).thenComparing(User::getAge));

list to map では、複数のフィールドキーを持つマップ構造を使うことがあります。通常のキーの文字列連結に加えて、apache commons のマップ構造 MultiKeyMap を複数のフィールドキーの形で使用することができます。

6. ソート

Sortによる単一フィールド・複数フィールドの並べ替え

userList.sort(Comparator.compare(User::getId,Comparator.nullsLast(Comparator.naturalOrder())).thenComparing(User::getAge, Comparator.nullsLast(Comparator.naturalOrder())));

これは実際にはヌル安全性の問題があるので、次のように変更することが推奨されます。

        //de-duplication
        List<Long> idList = new ArrayList<Long>();
        idList.add(1L);
        idList.add(1L);
        idList.add(2L);
        List<Long> distinctIdList = idList.stream().distinct().collect(Collectors.toList());

7. 重複排除

重複排除は明確な方法で行うことができる

List<AddOutboundNoticeDetailsBatchVo> entryDetailsBatchDistinctBatchIdList = entryDetailsBatchList.stream().filter( distinctByKey(b -> b.getMaterialBatchNumberId())).collect(Collectors.toList());
                
//distinctByKey defined by themselves
    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;
    }


属性に対して重複を排除する

        // Get a field of a list object to assemble a new list
        List<Long> userIdList = userList.stream().map(a -> a.getId()).collect(Collectors.toList());

8. リストのフィールドを取得し、新しいリストを組み立てる

addList.stream().forEach(a -> a.setDelFlag("0"));

9. リストのリストフィールドを同じ値に一括設定する

List<TimePeriodDate> timePeriodDateList1 = calendarModelVoList.stream().map(p->{TimePeriodDate e = new TimePeriodDate(); e. setStartDate(p.getBegin()); e.setEndDate(p.getEnd()); return e;}).collect(Collectors.toList());
                       

10. 異なるエンティティのリストコピー

List<TimePeriodDate> timePeriodDateList1 = calendarModelVoList.stream().map(p->{TimePeriodDate e = new TimePeriodDate(); e. setStartDate(p.getBegin()); e.setEndDate(p.getEnd()); return e;}).collect(Collectors.toList());