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
신분증만 보고.하지만 얼마나 많은 사람이User
s는 필터와 일치했다.
필터 라인을 다음으로 변경:
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
에 의해
- 우리의 물건을 수집하는 것은
List
…과 함께Collectors.toList()
수집가 - 끝에 추가 피니셔를 적용하여 단일 요소를 반환하거나 또는 다음 요소를 던짐
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
또는 그 이상1
의 Stream
그렇지 않으면 단일 값을 반환한다.
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;
}
}
- 위의 코드에서 조건에 따라 목록에서 둘 이상의 참이 발견되면 예외를 통과한다.
- 서버 측에서 로그를 쉽게 유지 관리할 수 있기 때문에 오류가 발생하면 사용자 지정 메시지가 표시됨.
- 목록에 있는 요소의 N번째 수로부터 목록에 있는 요소 중 하나 이상이 해당 순간에 참 상태를 가지는 요소가 둘 이상 있을 경우, 예외를 통해 하나의 요소만 참 조건을 갖기를 원할 뿐이다.
- 이 모든 것을 가져온 후, 리스트에서 한 요소를 가져와 다른 객체에 저장하기 위해 get()를 사용한다.
- 이 다다닥다닥과 같은 선택사항을 추가하기를 .
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
'Programing' 카테고리의 다른 글
vue에서 구성 요소를 동적으로 로드하는 방법 (0) | 2022.05.19 |
---|---|
프로젝트를 Vue2에서 Vue3로 업그레이드하는 가장 좋은 방법 (0) | 2022.05.18 |
타사 라이브러리에서 vue.js 구성 요소 확장 (0) | 2022.05.18 |
돌연변이와 게터 사이의 상태 손실 - Vuex/Nuxt (0) | 2022.05.18 |
Vue.js 2/Vuex - 스크롤바가 어떤 이유로 인해 파손됨 (0) | 2022.05.18 |