질문
오늘날 인터넷 기술은 성숙하고 점점 더 많은 사람들이 탈 중앙화, 배포 및 스트림 컴퓨팅 경향이 있으며, 이는 Java 측의 데이터베이스 측면에서 수행 된 많은 것들을 넣었습니다. 오늘 누군가가 데이터베이스 필드에 색인이 없는지 물었습니다. 필드를 기반으로 어떻게 중복 제거되어야합니까? 모두가 Java를 사용하기로 동의하지만 어떻게해야합니까?
답변
갑자기 나는 전에 무거운 무거운 무게를 제거하기 위해 목록에 쓴 기사를 기억하고 그것을 발견하고 읽었습니다. 이 방법은 해시 코드를 다시 작성하고 목록의 객체의 메소드를 해시 세트에 던지고 꺼내는 것입니다. 이것은 내가 자바를 처음 배웠을 때 사전처럼 쓴 대답입니다. 예를 들어, 인터뷰를 할 때 3 년 동안 Java에 있었던 사람들은 SET과 HASHMAP의 차이점을 암기 할 수 있지만이를 구현하는 방법을 모릅니다. 다시 말해, 초보자는 특성을 암기합니다. 그러나 실제로 프로젝트에서 그것을 사용하는 경우, 그것이 사실인지 확인해야합니다. 보증은 쓸모가 없기 때문에 결과 만 믿을 수 있습니다. 해시 세트가 무거운 무게를 제거하는 데 어떻게 도움이되는지 알아야합니다. 생각하면 해시 세트없이 무거운 하중을 제거 할 수 있습니까? 가장 간단하고 가장 직접적인 방법은 매번 히스토리 데이터와 비교하여 큐의 꼬리에 다른 경우 큐의 꼬리에 삽입하는 것입니다. 해시 세트는이 과정을 속도로 만듭니다.
먼저, 객체 사용자에게 정렬하도록합니다
@data@builder@allargsconstructorpublic class user {private integer id; 개인 문자열 이름;} list <user> user = lists.newarrayList (새 사용자 (1, "a"), 새 사용자 (1, "b"), 새 사용자 (2, "b"), 새 사용자 (1, "a");목표는 중복 ID없이 사용자를 꺼내는 것입니다. 싸움을 방지하기 위해 규칙을 제시합니다. 마음대로 고유 한 ID가있는 데이터를 꺼내고 ID가 동일 할 때 어떤 계산이 계산되는지 양심적 일 필요는 없습니다.
가장 직관적 인 방법을 사용하십시오
이 방법은 빈 목록을 사용하여 트래버스 데이터를 저장하는 것입니다.
@testpublic void dis1 () {list <user> result = new LinkedList <> (); for (user user : user) {boolean b = result.stream (). AnyMatch (u-> u.getId (). Equals (user.getId ()); if (! b) {result.add (user); }} system.out.println (결과);}해시 세트를 사용하십시오
이 기능을 암기 한 사람은 해시셋이 무거운 무게를 제거 할 수 있다는 것을 알고 있으므로 무거운 무게를 제거하려면 어떻게해야합니까? 조금 더 깊고 해시 코드에 따라 방법을 암기하고 방법과 같습니다. 그렇다면이 두 가지를 기반으로하는 방법은 무엇입니까? 소스 코드를 읽지 않은 사람들은 계속할 수 없으며 인터뷰는 여기서 끝납니다.
실제로 Hashset은 Hashmap에 의해 구현됩니다 (소스 코드를 본 적이 없으며 Hashmap의 키가 Hashset에 의해 구현된다고 항상 직관적으로 생각했습니다. 여기서 설명을 확장하지 않고 구성 방법을보고 이해할 해시 세트 방법을 추가하십시오.
public hashset () {map = new hashmap <> ();}/*** 분명히, 존재하는 경우, 존재하지 않으면 존재하지 않으면 true*/public boolean add (e e) {return map.put (e, present) == null;}를 반환합니다.그런 다음 해시 셋의 반복이 해시 맵을 기반으로 구현되고 해시 맵의 구현은 해시 코드와 동등한 방법에 완전히 의존한다는 것을 알 수 있습니다. 이제 완전히 열렸습니다. 해시셋을 사용하려면 두 가지 방법에 대해 낙관적이어야합니다.
이 질문에서는 ID를 기반으로 중복 제거해야하므로 비교 기준은 ID입니다. 수정은 다음과 같습니다.
@overridepublic boolean equals (Object o) {if (this == o) {return true; } if (o == null || getClass ()! = O.getClass ()) {return false; } user user = (사용자) o; return objects.equals (id, user.id);}@atriadepublic int hashcode () {return objects.hash (id);} // hashcoderesult = 31 * result + (element == null? 0 : element.hashcode ());그중에서도 객체는 배열의 해시 코드를 호출하고 내용은 위와 같습니다. 31을 곱한 x << 5-x.
최종 구현은 다음과 같습니다.
@testpublic void dis2 () {set <user> result = new Hashset <> (사용자); System.out.println (결과);}Java 스트림을 사용하여 제거하십시오
초기 질문으로 돌아가서,이 질문을하는 이유는 데이터베이스 쪽을 Java쪽에 다시 구하려면 10 만 조각과 같이 데이터의 양이 비교적 클 수 있기 때문입니다. 빅 데이터의 경우 스트림 관련 기능을 사용하는 것이 가장 쉽습니다. 스트림이 뚜렷한 기능을 제공하는 것처럼. 그렇다면 어떻게 사용해야합니까?
user.parallelStream (). CORENT (). foreach (System.out :: println);
나는 Lambda를 매개 변수로 보지 못했습니다. 즉, 맞춤 조건이 제공되지 않았습니다. 다행히 Javadoc은 중복 제거 표준을 표시했습니다.
이 스트림의 고유 한 요소 ({@link object#equals (object)}에 따라)로 구성된 스트림을 반환합니다.우리는 또한이 원칙을 암기해야한다는 것을 알고 있습니다. 동등한 반환이 true를 반환 할 때 해시 코드의 반환 값은 동일해야합니다. 이것은 암기 할 때 논리적으로 혼란 스럽지만 해시 맵의 구현 방법을 이해하는 한 대화하기가 어렵지 않을 것입니다. Hashmap은 먼저 해시 코드 방법에 따라 위치한 다음 Equals 메소드를 비교합니다.
따라서 뚜렷한 중복 제거를 달성하려면 해시 코드를 무시하고 기본값을 사용하지 않는 한 메소드와 동일해야합니다.
그렇다면 왜 이렇게합니까? 클릭하고 구현을 살펴보십시오.
<p_in> 노드 <t> 감소 (PipelineHelper <T> 헬퍼, 스플리렉트 레이터 <P_IN> 스플 라테이터) {// 스트림이 정렬되면 다음과 같은 순서가 정렬 된 순서 terminalop <t, 링크드 하시 세트 = ReduceOps = ReduceOps. linkedhashset :: add, linkedhashset :: addall); 리턴 노드 .node (retedop.evaluateparallel (헬퍼, 스플리터 레이터));}내부 구현은 줄임으로써 달성됩니다. 감소를 생각할 때, 당신은 즉시 혼자서 고유 한 비키를 구현하는 방법을 생각합니다. 나는 단지 감소를 사용하면되며 계산 부분은 스트림 요소를 내장 해시 맵과 비교하고 존재하는 경우 건너 뛰고 없으면 설치하는 것입니다. 실제로, 아이디어는 처음에 가장 간단한 방법입니다.
@testpublic void dis3 () {user.parallelstream (). 필터 (ConseTBykey (user :: getId)) .foreach (system.out :: println);} public static <t> 술어 <T> ConstrByKey (function <? super t,?> keyExtractor) {set <botor> see = concurrenthashmap.newkeyset (); return t-> see.add (keyextractor.apply (t));}물론, 병렬 스트림이라면, 촬영 한 스트림은 반드시 첫 번째 스트림이 아니라 무작위입니다.
위의 방법은 가장 잘 발견되며 비 침습적입니다. 그러나 당신이 뚜렷한 것을 사용해야한다면. 해시 코드 만 다시 쓸 수 있으며 해시 세트 방법과 같습니다.
요약
이런 것들을 직접 사용할 수 있는지 만 연습 할 수 있습니다. 그렇지 않으면, 실제로 사용하고 싶을 때 한 번에 꺼내는 것이 어려울 것입니다. 그렇지 않으면 위험을 감수 할 것입니다. 그리고 대담하게 사용하려면 규칙과 구현 원칙을 이해해야합니다. 예를 들어, LinkedHashset 및 Hashset의 구현은 어떻게 다릅니 까?
간단한 LinkedHashSet 소스 코드와 함께 첨부 :
공개 클래스 LinkedHashset <e>는 해시 세트 <e> 구현 세트 <e>, clonable, java.io.serializable {private static final long serialversionuid = -285166767971038690L; public linkedhashset (int initialcapacity, float loadfactor) {super (InitialCapacity, loadfactor, true); } public LinkedHashset (int initialcapacity) {super (초기 범위, .75f, true); } public LinkedHashset () {super (16, .75f, true); } public linkedhashset (collection <? extends e> c) {super (math.max (2*c.size (), 11), .75f, true); addall (c); } @override public spliterator <e> spliterator () {return spliterator.spliterator (this, spliterator.distinct | spliterator.ordered); }}다시 채우다:
Java의 목록 수집에서 중복 데이터를 제거하는 방법
1. 목록의 모든 요소를 루프 한 다음 복제를 삭제합니다.
공개 정적 목록 removedUplice (list list) {for (int i = 0; i <list.size () -1; i ++) {for (int j = list.size () -1; j> i; }}} 리턴 목록; } 2. 해시 세트를 통해 중복 요소를 시작합니다
public static list removeduplication (목록 목록) {Hashset h = new Hashset (list); list.clear (); list.addall (h); 반환 목록; }3. 순서를 유지하려면 ArrayList에서 중복 요소를 삭제하십시오
// ArrayList에서 중복 요소를 삭제하려면 ORDER ORDER CUPULL STATIC AVOID removedUplyWithOrder (List List) {SET SET = NEW HASHSET (); list newlist = new arraylist (); for (iterator iter = list.iterator (); iter.hasnext ();) {개체 요소 = iter.next (); if (set.add (element)) newlist.add (요소); } list.clear (); list.addall (신약); System.out.println ( "복제 제거" + 목록); }4. 목록의 개체를 반복하고 List.contain ()을 사용하고 존재하지 않으면 다른 목록 컬렉션에 넣으십시오.
public static list removedUplication (목록 목록) {list listtemp = new arrayList (); for (int i = 0; i <list.size (); i ++) {if (! listtemp.contains (list.get (i))) {listtemp.add (list.get (i)); }} return listtemp; }