ListUtils的简单集合操作和原理

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源码

  • ListUtils类
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;
    }

}
  • demo
@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注解,避免造成不必要的麻烦
posted @ 2018-10-18 15:36  月下小魔王  阅读(8891)  评论(0编辑  收藏  举报