1 Maven依賴
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
2 ListUtil實現集合的異或功能
- 集合A(1,2,3)異或 集合B(2,3,4)等於 (1,4)
@Test
public void test1() {
List<Integer> listA = new ArrayList<>();
listA.add(1);
listA.add(2);
listA.add(3);
List<Integer> listB = new ArrayList<>();
listB.add(2);
listB.add(3);
listB.add(4);
long start = System.currentTimeMillis();
List<Integer> sub1 = ListUtils.subtract(listA, listB); // list1與list2的差集
List<Integer> sub2 = ListUtils.subtract(listB, listA); // list2與list1的差集
List<Integer> union = ListUtils.union(sub1, sub2); // sub1與sub2的並集
long diff = System.currentTimeMillis() - start;
log.info("sub1:{}", sub1); // sub1:[1]
log.info("sub2:{}",sub2); // sub2:[4]
log.info("runtime:{}ms,unionList:{}", diff, union);// runtime:5ms, unionList:[1, 4]
}
3 ListUtil源碼
public class ListUtils {
// 依賴HashBag類
public static <E> List<E> subtract(final List<E> list1, final List<? extends E> list2) {
final ArrayList<E> result = new ArrayList<E>();
// 涉及HashBag,下面進行講解
final HashBag<E> bag = new HashBag<E>(list2);
for (final E e : list1) {
if (!bag.remove(e, 1)) {
result.add(e);
}
}
return result;
}
// List的addAll功能
public static <E> List<E> union(final List<? extends E> list1, final List<? extends E> list2) {
final ArrayList<E> result = new ArrayList<E>(list1);
result.addAll(list2);
return result;
}
}
4 Bag接口:背包
- 從出發點(Bag接口)去思考(HashBag類)具體實現中操作的意思
- 以一個實際生活的例子做解釋:一個放球的背包
public interface Bag<E> extends Collection<E> {
// 當object為"紅",背包中放了多少紅球
int getCount(Object object);
// 當object為"藍",往背包放一個藍球
boolean add(E object);
// 當object為"藍",往背包放nCopies個藍球
boolean add(E object, int nCopies);
// 當object為"紅",把所有紅球從背包中取出
boolean remove(Object object);
// 當object為"紅",把nCopies個紅球從背包中取出
boolean remove(Object object, int nCopies);
// 背包中有哪幾種球:如:1個紅 2個藍 -> 紅 藍
Set<E> uniqueSet();
// 總共多少球
int size();
// ...省略一些接口
}
5 HashBag源碼與Demo
public class HashBag<E> extends AbstractMapBag<E> implements Serializable {
public HashBag() {
super(new HashMap<E, MutableInteger>());
}
public HashBag(final Collection<? extends E> coll) {
this();
super.addAll(coll); // AbstractMapBag的addAll方法
}
}
// 內部由一個Map實現
public abstract class AbstractMapBag<E> implements Bag<E> {
/** 用一個Map來放球,key為球,value為數量 */
private transient Map<E, MutableInteger> map;
/** 背包有多少球 */
private int size;
/** fail fast iterators的安全機制 */
private transient int modCount;
/** 有哪幾種球的 */
private transient Set<E> uniqueSet;
public boolean add(final E object) {
return add(object, 1);
}
// 很簡單:如果map包含object,則取出value,並加上nCopies;不包含就新建即可。
public boolean add(final E object, final int nCopies) {
modCount++;
if (nCopies > 0) {
final MutableInteger mut = map.get(object);
size += nCopies;
if (mut == null) {
map.put(object, new MutableInteger(nCopies));
return true;
}
mut.value += nCopies;
return false;
}
return false;
}
// 遍歷刪除
public boolean addAll(final Collection<? extends E> coll) {
boolean changed = false;
final Iterator<? extends E> i = coll.iterator();
while (i.hasNext()) {
final boolean added = add(i.next());
changed = changed || added;
}
return changed;
}
public boolean remove(final Object object, final int nCopies) {
final MutableInteger mut = map.get(object);
if (mut == null) return false; // 不包含該object則返回false
if (nCopies <= 0) return false;
modCount++;
if (nCopies < mut.value) { // 如果背包中的數量比刪除的數量多則相減
mut.value -= nCopies;
size -= nCopies;
} else { // 如果背包中的數量比刪除的數量少則直接刪完
map.remove(object);
size -= mut.value;
}
return true;
}
// 集合類的toString都需要用到StringBuilder進行append,這樣可以提升性能
public String toString() {
if (size() == 0) {
return "[]";
}
final StringBuilder buf = new StringBuilder();
buf.append('[');
final Iterator<E> it = uniqueSet().iterator();
while (it.hasNext()) {
final Object current = it.next();
final int count = getCount(current);
buf.append(count);
buf.append(':');
buf.append(current);
if (it.hasNext()) {
buf.append(',');
}
}
buf.append(']');
return buf.toString();
}
public int getCount(final Object object) {
final MutableInteger count = map.get(object);
return count == null ? 0 : count.value;
}
}
@Slf4j // lombok
public class Main {
public static void main(String[] args) {
HashBag<Integer> bag = new HashBag<>();
bag.add(1);
bag.add(2);
bag.add(3);
System.out.println(bag); // [1:1,1:2,1:3] 第一個值為出現次數,第二個value值
boolean remove2 = bag.remove(2, 1); // 刪除2值1次,發現有並刪除返回true
boolean remove4 = bag.remove(4, 1); // 刪除4值1次,發現沒有返回false
// remove2:true remove4:false
log.info("remove2:{} remove4:{}",remove2,remove4);
System.out.println(bag); // [1:1,1:3]
}
}
6 HashCode重寫
- 設計Hash的容器類都需要重寫equals方法和hashCode方法來實現自己希望的邏輯,如:HashBag、HashSet或者希望自定義對象作為HashMap的值
public class Main {
public static void main(String[] args) {
Student student1 = new Student("andy", 11,99.5);
Student student2 = new Student("andy", 11,99.5);
System.out.println(student1.equals(student2)); // true
Set<Student> set = new HashSet<>();
set.add(student1);
set.add(student2);
System.out.println(set.size()); // 1
}
@Getter
@Setter
static class Student {
private String name;
private int age;
private double score;
public Student(String name, int age,double score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj.getClass() != this.getClass()) return false;
Student student = (Student) obj;
return student.name.equals(this.name)
&& student.age == this.age
&& student.score == this.score;
}
@Override
public int hashCode() {
int hash = 17;
hash = hash * 31 + name.hashCode();
hash = hash * 31 + age;
hash = hash * 31 + Double.valueOf(score).hashCode();
return hash;
}
}
}
- 注意:lombok的@Data注解會自動幫你重寫equals和hashCode方法,不需要時請不要使用@Data注解,避免造成不必要的麻煩