source

스트림을 사용하여 사용자 지정 비교기를 사용하여 TreeSet에 수집

factcode 2022. 9. 21. 23:24
반응형

스트림을 사용하여 사용자 지정 비교기를 사용하여 TreeSet에 수집

Java 8에서 일하면서TreeSet다음과 같이 정의됩니다.

private TreeSet<PositionReport> positionReports = 
        new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp));

PositionReport는 다음과 같이 정의된 비교적 단순한 클래스입니다.

public static final class PositionReport implements Cloneable {
    private final long timestamp;
    private final Position position;

    public static PositionReport create(long timestamp, Position position) {
        return new PositionReport(timestamp, position);
    }

    private PositionReport(long timestamp, Position position) {
        this.timestamp = timestamp;
        this.position = position;
    }

    public long getTimestamp() {
        return timestamp;
    }

    public Position getPosition() {
        return position;
    }
}

이거 잘 돼.

여기서 엔트리를 삭제합니다.TreeSet positionReports어디에timestamp어느 값보다 오래된 값입니다.그러나 나는 이것을 표현하기 위한 올바른 Java 8 구문을 찾을 수 없다.

이 시도는 실제로 컴파일되지만TreeSet정의되지 않은 비교기:

positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(Collectors.toCollection(TreeSet::new))

어떻게 표현하면 좋을까요?TreeSet같은 비교기로Comparator.comparingLong(PositionReport::getTimestamp)?

이런 생각이 들었죠

positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(
            Collectors.toCollection(
                TreeSet::TreeSet(Comparator.comparingLong(PositionReport::getTimestamp))
            )
        );

그러나 이것은 메서드 참조에 대한 올바른 구문을 컴파일하지 않습니다.

메서드 참조는 만족시키려는 대상의 모양에 맞는 메서드(또는 생성자)가 있는 경우 사용할 수 있습니다.이 경우 대상 쉐이프가 다음과 같기 때문에 메서드 참조를 사용할 수 없습니다.Supplier인수는 필요 없습니다만, 가지고 계신 것은TreeSet인수를 사용하는 컨스트럭터입니다.이 인수는 어떤 인수가 필요한지 지정해야 합니다.따라서 덜 간결한 방법을 사용하여 람다 식을 사용해야 합니다.

TreeSet<Report> toTreeSet(Collection<Report> reports, long timestamp) {
    return reports.stream().filter(report -> report.timestamp() >= timestamp).collect(
        Collectors.toCollection(
            () -> new TreeSet<>(Comparator.comparingLong(Report::timestamp))
        )
    );
}

다음 코드를 사용하면 간단합니다.

    positionReports = positionReports
        .stream()
        .filter(p -> p.timestamp >= oldestKept)
        .collect(
            Collectors.toCollection(()->new TreeSet<>(Comparator.comparingLong(PositionReport::getTimestamp)
)));

마지막에 SortedSet으로 변환할 수 있습니다(추가 복사본이 필요 없는 경우).

positionReports = positionReports
                .stream()
                .filter(p -> p.getTimeStamp() >= oldestKept)
                .collect(Collectors.toSet());

return new TreeSet(positionReports);

수집에는 스트림을 사용하지 않고 다음과 같은 방법이 있습니다.default boolean removeIf(Predicate<? super E> filter). "Javadoc" 참조.

따라서 코드는 다음과 같습니다.

positionReports.removeIf(p -> p.timestamp < oldestKept);

Tree Set의 문제는 아이템을 정렬하기 위해 사용하는 비교기가 아이템을 세트에 삽입할 때 중복을 검출하기 위해서도 사용된다는 것입니다.따라서 비교기 함수가 2개의 항목에 대해 0일 경우 중복된 항목으로 간주하여 하나를 잘못 폐기합니다.

중복검출은 다른 올바른 hashCode 메서드로 수행해야 합니다.모든 속성(이 예에서는 ID와 이름)을 고려한 해시 코드와의 중복을 방지하고 아이템을 취득할 때(이 예에서는 이름만으로 정렬) 단순 정렬 목록을 반환하기 위해 단순한 해시 세트를 사용하는 것이 좋습니다.

public class ProductAvailableFiltersDTO {

    private Set<FilterItem> category_ids = new HashSet<>();

    public List<FilterItem> getCategory_ids() {
        return category_ids.stream()
            .sorted(Comparator.comparing(FilterItem::getName))
            .collect(Collectors.toList());
    }

    public void setCategory_ids(List<FilterItem> category_ids) {
        this.category_ids.clear();
        if (CollectionUtils.isNotEmpty(category_ids)) {
            this.category_ids.addAll(category_ids);
        }
    }
}


public class FilterItem {
    private String id;
    private String name;

    public FilterItem(String id, String name) {
        this.id = id;
        this.name = name;
    }

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof FilterItem)) return false;
        FilterItem that = (FilterItem) o;
        return Objects.equals(getId(), that.getId()) &&
                Objects.equals(getName(), that.getName());
    }

    @Override
    public int hashCode() {

        return Objects.hash(getId(), getName());
    }
}
positionReports = positionReports.stream()
                             .filter(p -> p.getTimeStamp() >= oldestKept)
                             .collect(Collectors.toCollection(() -> new 
TreeSet<PositionReport>(Comparator.comparingLong(PositionReport::getTimestamp))));

언급URL : https://stackoverflow.com/questions/21697349/using-streams-to-collect-into-treeset-with-custom-comparator

반응형