Programing

Java 스트림을 1개 요소만 필터링

c10106 2022. 5. 18. 21:52
반응형

Java 스트림을 1개 요소만 필터링

Java 8s를 사용하여 다음에서 요소를 찾으려고 한다.LinkedList그러나 필터 기준과 일치하는 항목이 하나뿐임을 보장하고 싶다.

다음 코드 사용:

public static void main(String[] args) {

    LinkedList<User> users = new LinkedList<>();
    users.add(new User(1, "User1"));
    users.add(new User(2, "User2"));
    users.add(new User(3, "User3"));

    User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
    System.out.println(match.toString());
}

static class User {

    @Override
    public String toString() {
        return id + " - " + username;
    }

    int id;
    String username;

    public User() {
    }

    public User(int id, String username) {
        this.id = id;
        this.username = username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

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

    public String getUsername() {
        return username;
    }

    public int getId() {
        return id;
    }
}

finds을 .User신분증만 보고.하지만 얼마나 많은 사람이Users는 필터와 일치했다.

필터 라인을 다음으로 변경:

User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();

던질 것이다NoSuchElementException(좋다!)

그래도 여러 경기가 있으면 실책을 했으면 좋겠다.이렇게 할 방법이 있을까?

사용자 정의 생성

public static <T> Collector<T, ?, T> toSingleton() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> {
                if (list.size() != 1) {
                    throw new IllegalStateException();
                }
                return list.get(0);
            }
    );
}

우리는 우리가 원하는 것을 만들곤 했다.Collector에 의해

  1. 우리의 물건을 수집하는 것은List…과 함께Collectors.toList()수집가
  2. 끝에 추가 피니셔를 적용하여 단일 요소를 반환하거나 또는 다음 요소를 던짐IllegalStateException만일list.size != 1.

사용 용도:

User resultUser = users.stream()
        .filter(user -> user.getId() > 0)
        .collect(toSingleton());

그런 다음 이 항목을 사용자 지정Collector예를 들어, 예외를 생성자에서 인수로 지정하고, 두 값을 허용하도록 수정하는 등의 작업을 원하는 만큼 수행하십시오.

그 대안은 거의 우아하지 않은 해결책:

다음과 같은 '해결책'을 사용할 수 있다.peek()그리고AtomicInteger하지만 정말로 그걸 사용해서는 안 돼.

대신 할 수 있는 것은 단지 그것을 수집하는 것이다.List, 다음과 같은 경우:

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
List<User> resultUserList = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.toList());
if (resultUserList.size() != 1) {
    throw new IllegalStateException();
}
User resultUser = resultUserList.get(0);

완전성을 위해 @prunge의 훌륭한 대답에 해당하는 '원라이너'가 여기에 있다.

User user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })
        .get();

이것은 스트림에서 유일하게 일치하는 요소를 얻으며,

  • NoSuchElementException냇물이 비어 있는 경우, 또는
  • IllegalStateException스트림에 일치하는 요소가 둘 이상 있는 경우

이 접근법의 변동은 예외를 조기에 던지는 것을 피하고 대신 결과를 다음과 같이 나타낸다.Optional 0 가 있을 도 포함하지 않음0개는 여러의 가가는 無(無)이다.

Optional<User> user1 = users.stream()
        .filter(user -> user.getId() == 1)
        .collect(Collectors.reducing((a, b) -> null));

관습 작성과 관련된 다른 답은 아마도 더 효율적일 것이다(Louis Wasserman, +1) 하지만 간결함을 원한다면, 나는 다음과 같은 것을 제안한다.

List<User> result = users.stream()
    .filter(user -> user.getId() == 1)
    .limit(2)
    .collect(Collectors.toList());

그런 다음 결과 목록의 크기를 확인하십시오.

if (result.size() != 1) {
  throw new IllegalStateException("Expected exactly one user but got " + result);
User user = result.get(0);
}

구아바는 여기서 옳은 일을 하는 것을 제공한다.하지만 스스로 해야 한다면 스스로 굴릴 수도 있다.Collector이를 위해:

<E> Collector<E, ?, Optional<E>> getOnly() {
  return Collector.of(
    AtomicReference::new,
    (ref, e) -> {
      if (!ref.compareAndSet(null, e)) {
         throw new IllegalArgumentException("Multiple values");
      }
    },
    (ref1, ref2) -> {
      if (ref1.get() == null) {
        return ref2;
      } else if (ref2.get() != null) {
        throw new IllegalArgumentException("Multiple values");
      } else {
        return ref1;
      }
    },
    ref -> Optional.ofNullable(ref.get()),
    Collector.Characteristics.UNORDERED);
}

의 것을 하는 것...혹은 당신 자신의 것을 사용하는 것.Holder대신에 타이프를 치다AtomicReference당신은 그것을 재사용할 수 있다.Collector네가 원하는 만큼

과바의 (소스 코드)를 사용한다.

그것은 당신이 원하는 것을 하고 던진다.IllegalArgumentException하천이 2개 이상의 원소와 a로 구성되는 경우NoSuchElementException냇물이 비어 있으면

사용량:

import static com.google.common.collect.MoreCollectors.onlyElement;

User match =
    users.stream().filter((user) -> user.getId() < 0).collect(onlyElement());

달리 하천에서 지지받지 않는 이상한 일을 할 수 있게 하는 '탈출 해치' 작전은 '탈출 해치'를 부탁하는 것이다.Iterator:

Iterator<T> it = users.stream().filter((user) -> user.getId() < 0).iterator();
if (!it.hasNext()) {
    throw new NoSuchElementException();
} else {
    result = it.next();
    if (it.hasNext()) {
        throw new TooManyElementsException();
    }
}

구아바에게는 편리한 치료법이 있다.Iterator원소가 0이거나 여러 개일 경우 던지는 유일한 원소를 얻을 수 있으며, 이 원소는 아래 n-1 라인을 대체할 수 있다.

갱신하다

@Holger의 논평에서 좋은 제안:

Optional<User> match = users.stream()
              .filter((user) -> user.getId() > 1)
              .reduce((u, v) -> { throw new IllegalStateException("More than one ID found") });

오리지널 해답

예외는 에 의해 던져진다.Optional#get하지만 도움이 되지 않는 요소가 하나 이상 있다면.다음과 같은 한 항목만 수락하는 컬렉션에서 사용자를 수집할 수 있다.

User match = users.stream().filter((user) -> user.getId() > 1)
                  .collect(toCollection(() -> new ArrayBlockingQueue<User>(1)))
                  .poll();

그것은 a를 던진다.java.lang.IllegalStateException: Queue full하지만 그건 너무 진부한 느낌이야.

또는 다음과 같은 옵션과 함께 감소를 사용할 수 있다.

User match = Optional.ofNullable(users.stream().filter((user) -> user.getId() > 1)
                .reduce(null, (u, v) -> {
                    if (u != null && v != null)
                        throw new IllegalStateException("More than one ID found");
                    else return u == null ? v : u;
                })).get();

절감 효과는 기본적으로 다음과 같다.

  • 사용자가 없는 경우 null
  • 하나만 있는 경우 사용자
  • 둘 이상 발견되면 예외를 두다.

그 결과는 선택사항으로 포장된다.

그러나 가장 간단한 해결책은 아마도 컬렉션을 수집하여 크기가 1인지 확인하고 유일한 요소를 얻는 것이 될 것이다.

나는 이 방법이 더 간단하다고 생각한다.

User resultUser = users.stream()
    .filter(user -> user.getId() > 0)
    .findFirst().get();

다른 방법은 감소를 사용하는 것이다: (이 예는 문자열을 사용하지만 다음을 포함한 모든 개체 유형에 쉽게 적용할 수 있다.User)

List<String> list = ImmutableList.of("one", "two", "three", "four", "five", "two");
String match = list.stream().filter("two"::equals).reduce(thereCanBeOnlyOne()).get();
//throws NoSuchElementException if there are no matching elements - "zero"
//throws RuntimeException if duplicates are found - "two"
//otherwise returns the match - "one"
...

//Reduction operator that throws RuntimeException if there are duplicates
private static <T> BinaryOperator<T> thereCanBeOnlyOne()
{
    return (a, b) -> {throw new RuntimeException("Duplicate elements found: " + a + " and " + b);};
}

그래서 에 관한 경우는.User다음을 수행했을 수 있음:

User match = users.stream().filter((user) -> user.getId() < 0).reduce(thereCanBeOnlyOne()).get();

사용량 감소

이것이 내가 찾은 가장 간단하고 유연한 방법이다(@prunge 답변 기준)

Optional<User> user = users.stream()
        .filter(user -> user.getId() == 1)
        .reduce((a, b) -> {
            throw new IllegalStateException("Multiple elements: " + a + ", " + b);
        })

이렇게 하면 다음과 같은 결과를 얻을 수 있다.

  • 선택 사항 - 항상 그렇듯이Optional.empty()
  • 둘 이상의 요소가 있는 경우 예외(결국 사용자 정의 유형/메시지 포함)

구아바는Collector이것을 위하여

a 사용:

public static <T> Collector<T, ?, Optional<T>> singleElementCollector() {
    return Collectors.collectingAndThen(
            Collectors.toList(),
            list -> list.size() == 1 ? Optional.of(list.get(0)) : Optional.empty()
    );
}

사용량:

Optional<User> result = users.stream()
        .filter((user) -> user.getId() < 0)
        .collect(singleElementCollector());

우리는 보통은 그것을 가정할 수 없기 때문에, a를 반환한다.Collection정확히 하나의 요소를 포함하는 것.이미 알고 있는 경우, 다음 연락처로 전화하십시오.

User user = result.orElseThrow();

이것은 전화를 건 사람에게 실수를 손대야 하는 부담을 준다.

축소 및 선택사항 사용

Fabio Bonfante 응답 내용:

public <T> T getOneExample(Collection<T> collection) {
    return collection.stream()
        .filter(x -> /* do some filter */)
        .reduce((x,y)-> {throw new IllegalStateException("multiple");})
        .orElseThrow(() -> new NoSuchElementException("none"));
}

RxJava(매우 강력한 반응형 확장 라이브러리)를 사용할 수 있다.

LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));

User userFound =  Observable.from(users)
                  .filter((user) -> user.getId() == 1)
                  .single().toBlocking().first();

단일 운영자는 한 명 이상의 사용자가 없을 경우 예외를 발생시킨다.

로서Collectors.toMap(keyMapper, valueMapper)던지기 합병을 사용하여 동일한 키로 여러 항목을 쉽게 처리:

List<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));

int id = 1;
User match = Optional.ofNullable(users.stream()
  .filter(user -> user.getId() == id)
  .collect(Collectors.toMap(User::getId, Function.identity()))
  .get(id)).get();

A를 받게 될 것이다.IllegalStateException중복 키의 경우그러나 마지막에 나는 코드를 더 잘 읽을 수 없을지 확신할 수 없다.if.

나는 그 두 수집가를 사용하고 있다.

public static <T> Collector<T, ?, Optional<T>> zeroOrOne() {
    return Collectors.reducing((a, b) -> {
        throw new IllegalStateException("More than one value was returned");
    });
}

public static <T> Collector<T, ?, T> onlyOne() {
    return Collectors.collectingAndThen(zeroOrOne(), Optional::get);
}

타사 라이브러리를 사용하는 것이 괜찮다면, 사이클롭스 스팀(및 단순 리액션)에서 두 가지 모두 단일 & 단일 옵션 연산자를 가지고 있다.

singleOptional()오다이가 로 0또는 그 이상1Stream그렇지 않으면 단일 값을 반환한다.

String result = SequenceM.of("x")
                          .single();

SequenceM.of().single(); // NoSuchElementException

SequenceM.of(1, 2, 3).single(); // NoSuchElementException

String result = LazyFutureStream.fromStream(Stream.of("x"))
                          .single();

singleOptional()돌아온다Optional.empty()에 값이 없거나 둘 이상의 값이 있는 경우Stream.

Optional<String> result = SequenceM.fromStream(Stream.of("x"))
                          .singleOptional(); 
//Optional["x"]

Optional<String> result = SequenceM.of().singleOptional(); 
// Optional.empty

Optional<String> result =  SequenceM.of(1, 2, 3).singleOptional(); 
// Optional.empty

공개 - 나는 두 도서관의 저자다.

 List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
Integer value  = list.stream().filter((x->x.intValue()==8)).findFirst().orElse(null);

Null 포인터 예외가 있을 것이기 때문에 원시 유형 대신 정수 유형을 사용해 왔다.이 예외를 처리하면...내 생각에 간결해 보인다;)

구아바나 코틀린을 사용하지 않으면 @skiwi와 @Neuron 대답에 기초한 해결책이 여기 있다.

users.stream().collect(single(user -> user.getId() == 1));

또는

users.stream().collect(optional(user -> user.getId() == 1));

어디에single그리고optional해당 수집기를 반환하는 정적으로 가져온 함수.

나는 필터링 논리를 수집기 안으로 이동시켰다면 더 간명해 보일 것이라고 생각했다.또한 다음 문자열을 삭제하면 코드에서 어떤 것도 손상되지 않음.filter.

코드의 요지는 https://gist.github.com/overpas/ccc39b75f17a1c65682c071045c1a079이다.

나 자신을 위해 샘플 코드를 시도했고, 여기에 그것을 위한 해결책이 있다.

User user = Stream.of(new User(2), new User(2), new User(1), new User(2))
            .filter(u -> u.getAge() == 2).findFirst().get();

및 사용자 클래스

class User {
    private int age;

public User(int age) {
    this.age = age;
}

public int getAge() {
    return age;
}

public void setAge(int age) {
    this.age = age;
 }
}
public List<state> getAllActiveState() {
    List<Master> master = masterRepository.getActiveExamMasters();
    Master activeMaster = new Master();
    try {
        activeMaster = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {
            throw new IllegalStateException();
        }).get();
        return stateRepository.getAllStateActiveId(activeMaster.getId());
    } catch (IllegalStateException e) {
        logger.info(":More than one status found TRUE in Master");
        return null;
    }
}
  1. 위의 코드에서 조건에 따라 목록에서 둘 이상의 참이 발견되면 예외를 통과한다.
  2. 서버 측에서 로그를 쉽게 유지 관리할 수 있기 때문에 오류가 발생하면 사용자 지정 메시지가 표시됨.
  3. 목록에 있는 요소의 N번째 수로부터 목록에 있는 요소 중 하나 이상이 해당 순간에 참 상태를 가지는 요소가 둘 이상 있을 경우, 예외를 통해 하나의 요소만 참 조건을 갖기를 원할 뿐이다.
  4. 이 모든 것을 가져온 후, 리스트에서 한 요소를 가져와 다른 객체에 저장하기 위해 get()를 사용한다.
  5. 이 다다닥다닥과 같은 선택사항을 추가하기를 .Optional<activeMaster > = master.stream().filter(status -> status.getStatus() == true).reduce((u, v) -> {throw new IllegalStateException();}).get();
User match = users.stream().filter((user) -> user.getId()== 1).findAny().orElseThrow(()-> new IllegalArgumentException());

@skiwi에서 영감을 받아 다음과 같은 방법으로 해결했다.

public static <T> T toSingleton(Stream<T> stream) {
    List<T> list = stream.limit(1).collect(Collectors.toList());
    if (list.isEmpty()) {
        return null;
    } else {
        return list.get(0);
    }
}

그런 다음:

User user = toSingleton(users.stream().filter(...).map(...));

이거 먹어봤어?

long c = users.stream().filter((user) -> user.getId() == 1).count();
if(c > 1){
    throw new IllegalStateException();
}

long count()
Returns the count of elements in this stream. This is a special case of a reduction and is equivalent to:

     return mapToLong(e -> 1L).sum();

This is a terminal operation.

출처: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html

참조URL: https://stackoverflow.com/questions/22694884/filter-java-stream-to-1-and-only-1-element

반응형