一、针对String类型进行去重
方式一:使用另一个list在添加元素的时候进行重复判断
/**
* 方式一:使用另一个list在添加元素的时候进行重复判断
*/
public static void test1() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
List<String> stringsTemp = new ArrayList<>();
for (String s : stringList) {
if (!stringsTemp.contains(s)) {
stringsTemp.add(s);
}
}
//垃圾收集器某个时刻回收该对象所占的内存空间
System.out.println(stringsTemp);
}
方式二:同一个list列表,两层循环,一层从前往后,一层从后往前,进行对比
/**
* 方式二:同一个list列表,两层循环,一层从前往后,一层从后往前,进行对比
*/
public static void test2() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
//因为Arrays.asList()方法返回的列表,其长度是初始的数组大小,是固定的,所以不能做add和remove操作,会抛出java.lang.UnsupportedOperationException异常
stringList = new ArrayList<>(stringList);
for (int i = 0; i < stringList.size() - 1; i++) {
for (int j = stringList.size() - 1; j > i; j--) {
if (stringList.get(i).equals(stringList.get(j))) {
stringList.remove(j);
}
}
}
System.out.println(stringList);
}
或者两层循环从前往后,一前一后进行对比,但是这种方式需要注意第二层循环中的下标问题
public static void test2_2() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
stringList = new ArrayList<>(stringList);
for (int i = 0; i < stringList.size() - 1; i++) {
for (int j = i + 1; j < stringList.size(); j++) {
if (stringList.get(i).equals(stringList.get(j))) {
stringList.remove(j);
//注意这里需要将下标回退一个,因为remove操作之后,j这个下标指向的就是j的原本的下一个数,如果不执行j--操作,则在j++的时候就会跳过这个数,造成这个数没有进行比较
j--;
}
}
}
System.out.println(stringList);
}
方式三:使用set去重
/**
* 方式三:使用set去重
*/
public static void test3() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
//使用TreeSet排序去重,得到的结果是排序之后的
//stringList = new ArrayList<>(new TreeSet<>(stringList));
//System.out.println("使用TreeSet去重,得到的结果是排序之后的:" + stringList);
//使用HashSet排序去重,得到的结果顺序是随机的
//stringList = new ArrayList<>(new HashSet<>(stringList));
//System.out.println("使用HashSet去重,得到的结果顺序是随机的:" + stringList);
//使用LinkedHashSet的排序去重,结果是原来的排序(推荐这种方式)
stringList = new ArrayList<>(new LinkedHashSet<>(stringList));
System.out.println("使用LinkedHashSet去重,得到的结果顺序是原来的:" + stringList);
}
方式四:使用Collections.frequency计算集合中某个值的重复数量然后进行去重
这个方法会统计每个元素的在集合中的个数,所以当集合中数据量特别大的时候,性能不好
/**
* 方法四:使用Collections.frequency计算集合中某个值的重复数量然后进行去重,这个方法会统计每个元素的在集合中的个数,所以性能不好
*/
public static void test4() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
stringList = new ArrayList<>(stringList);
Iterator<String> iterator = stringList.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (Collections.frequency(stringList, s) > 1) {
iterator.remove();
//不能使用这种方式删除集合中的元素,这种方式在循环过程中删除某个数据会报并发修改异常,是由于其底层实现机制,查看其源码,是由于modCount和expectedModCount的值不一致,抛出了ConcurrentModificationException
//stringList.remove(s);
}
}
System.out.println("使用Collections.frequency去重:" + stringList);
}
上面使用的是迭代器Iterator进行循环操作,当然我们也可以使用for循环,for循环这种方式可以使用stringList.remove(currentValue);来删除集合中的元素。
public static void test4_1() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
stringList = new ArrayList<>(stringList);
String currentValue;
for (int i = 0; i < stringList.size(); i++) {
currentValue = stringList.get(i);
if (Collections.frequency(stringList, currentValue) > 1) {
stringList.remove(currentValue);
}
}
System.out.println("使用Collections.frequency去重:" + stringList);
}
但是在foreach循环中或者使用迭代器Iterator进行循环的过程中都不能使用stringList.remove(currentValue)来删除集合中的元素,这种方式在循环过程中删除某个数据会报并发修改异常,如:
/**
* 不要使用这种方式在循环集合过程中删除集合中的数据
*/
public static void test4_2() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
stringList = new ArrayList<>(stringList);
for (String currentValue : stringList) {
if (Collections.frequency(stringList, currentValue) > 1) {
/**
* 这种方式在循环过程中删除某个数据会报并发修改异常,是由于其底层实现机制,查看其源码,是由于modCount和expectedModCount的值不一致,抛出了ConcurrentModificationException
* 可以使用上面两种方式来在循环过程中删除集合中的某个数据
*/
stringList.remove(currentValue);
}
}
System.out.println("foreach循环过程中删除数据会报并发修改异常,这段话不会输出:" + stringList);
}
输出结果:
这是由于其底层实现机制,查看其源码,是由于modCount和expectedModCount的值不一致,抛出了ConcurrentModificationException。
所以我们可以使用上面两种方式来在循环过程中删除集合中的某个数据,即当Iterator过程中使用iterator.remove();方法和使用for循环方式。
方式五:使用stream()去重
/**
* 方式五:使用stream()去重
*/
public static void test5() {
List<String> stringList = Arrays.asList("aa", "a", "AA", "aa", "bb", "cc", "a", "bb", "AA");
stringList = stringList.stream().distinct().collect(Collectors.toList());
System.out.println(stringList);
}
二、对自定义的对象进行去重
自定义一个对象User,然后重写其equals()和hashCode()方法
package com.hungteshun;
import java.util.Objects;
/**
* @author hungteshun
* @description:
* @date 2018/12/4 22:31
*/
public class User {
private String id;
private Integer age;
private String name;
public User() {
}
public User(String id, Integer age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
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 == null || getClass() != o.getClass())
return false;
final User user = (User) o;
return Objects.equals(getId(), user.getId()) &&
Objects.equals(getAge(), user.getAge()) &&
Objects.equals(getName(), user.getName());
}
@Override
public int hashCode() {
return Objects.hash(getId(), getAge(), getName());
}
@Override
public String toString() {
return "User{" +
"id='" + id + '\'' +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
为什么要重写这两个方法呢?
因为在两个对象进行比较的时候,通过equals()方法我们就可以知道两个对象是否指向的是同一个对象或者对象所指向的内存地址中存放的内容是否相同。而一般地,我们重写了equals()方法,就尽量重写hashCode()方法,这是一个良好的编程习惯,至于equals()方法和hashCode()方法之间的关系,可以查看我的另外一篇博客。
我们可以查看Objects.equals()方法的源码:
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
这里a==b比较的是两个对象所指向的地址引用是否相同,即是否是指向同一个对象,如果不是,则继续判断对象a不为null,且调用对象a的equals方法,这里我们传入的是String类型的对象和Integer类型的对象,而String类型的对象中equals()已经重写了
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
先比较的是对象是否指向同一个地址,如果不是,则再比较的是对象所指向的地址中存放的内容是否相同
而Integer类型的对象中重写的equals()方法源码如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
比较的是整数的大小。
然后查看Objects.hash()方法的源码
public static int hash(Object... values) {
return Arrays.hashCode(values);
}
继续查看Arrays.hashCode()方法的源码
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
然后看一下String类的hashCode()方法的实现
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
而Integer类的hashCode()方法的实现如下:
public int hashCode() {
return Integer.hashCode(value);
}
继续查看
public static int hashCode(int value) {
return value;
}
重写好了equals()方法和hashCode()方法之后,我们就可以进行去重操作了
package com.hungteshun;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* @author hungteshun
* @description:
* @date 2018/12/04 22:37
*/
public class Main {
public static void main(String[] args) {
User user1 = new User("1",23,"大葱一号");
User user2 = new User("2",24,"大葱一号");
User user3 = new User("1",23,"大葱一号");
User user4 = new User("1",23,"大葱二号");
User user5 = new User("3",24,"大葱一号");
User user6 = new User("1",23,"大葱一号");
List<User> userList= new ArrayList<>();
userList.add(user1);
userList.add(user2);
userList.add(user3);
userList.add(user4);
userList.add(user5);
userList.add(user6);
//通过hashSet进行去重
//Set<User> userSet = new HashSet<>(userList);
//userSet.stream().forEach(System.out::println);
//使用流的distinct()方法进行去重
userList.stream().distinct().forEach(System.out::println);
}
}
输出结果为:
User{id='1', age=23, name='大葱一号'}
User{id='2', age=24, name='大葱一号'}
User{id='1', age=23, name='大葱二号'}
User{id='3', age=24, name='大葱一号'}