List与Set

List

List继承了Collection,是有序的列表。

  • 可以允许重复的对象
  • 可以插入多个null元素
  • 是一个有序容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序

常用的实现类有 ArrayList、LinkedList 和 Vector。
①ArrayList是基于数组实现的,是一个数组队列。可以动态的增加容量
②LinkedList是基于链表实现的,是一个双向循环列表。可以被当做堆栈使用
③Vector是基于数组实现的,是一个矢量队列,是线程安全的

Set

Set可以存储多个对象,但并不会记住元素的存储顺序,也不允许集合中有重复元素。

  • 不允许重复对象
  • 无序容器,你无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序
  • 只允许一个 null 元素

常用的实现类有HashSet、LinkedHashSet、TreeSet。
①HashSet基于HashMap 实现,保证元素唯一性的方式依赖于:hashCode()与equals()方法。元素的存与取的顺序不能保证一致。允许放null值,只能允许一个null
②LinkedHashSet元素唯一不能重复,底层结构是哈希表结构 + 链表结构,元素的存与取的顺序一致
③TreeSet实现了 SortedSet 接口,本质是一个"有序的,并且没有重复元素"的集合,它是二叉树实现的。TreeSet中的元素支持2种排序方式:自然排序或者根据创建TreeSet 时提供的Comparator 进行排序。不允许放入null值

Set去重

HashSet去重
   自定义对象不会自动去重需要重写equals和hashcode方法,去重系统类的对象时不用重写。去重:当添加到Set的对象 HashCode码不相同时不会调用equals方法,对象直接存到Set集合中;HashCode相同时才会调用equals方法查看是否是同一个对象。

LinkedHashSet去重
  有序的HashSet(按照存入集合的顺序打印)

TreeSet去重
  TreeSet存入对象打印时需要实现Comparable接口,重写接口中的 compareTo()方法

JDK1.8中HashSet的数据结构:

/**
 * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
 * default initial capacity (16) and load factor (0.75).
 */
 public HashSet() {
    map = new HashMap<>();
 }

可见new HashSet()操作实际上是new HashMap<>(),可见底层是以HashMap来实现的。
HashSet.add方法:

public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

HashMap.add方法:

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

判断插入的key是否存在,要判断两点①hash值是否相同;②对应的值是否相同,前者要看hashCode()方法,后者要看equal()方法。

下面对于基本的数据类型和自定义类类型在计算hashCode和equal的区别:

java.lang.Integer.equals():两个对象对应的值一致则返回true
public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

java.lang.String.equals():两个字符串对应的值一致则返回true
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;//anObject不是String实例,那么返回false
}

java.lang.Object.equals():两个对象的引用是否一致,即两个的对象是否是同一个。
public boolean equals(Object obj) {
    return (this == obj);
}
可见对于java.lang.Object.equals()来讲,两个new出来的对象肯定是不一致的,
那么在HashMap数据结构中不会被判定成相同的对象(尽管值相同)。

下面再看看hashCode的源码:

java.lang.Integer.hashCode():
@Override
public int hashCode() {
    return Integer.hashCode(value);
}
 public static int hashCode(int value) {
    return value;
}

java.lang.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;
}

java.lang.Object.hashCode():
public native int hashCode();

JDK8的默认hashCode的计算方法是通过和当前线程有关的一个随机数+三个确定值,运用Marsaglia’s xorshift schema随机数算法得到的一个随机数。因此,以上可以看到Integer和String也都是根据具体的value值来计算hashCode,那么尽管两个引用不同但是值相同的对象,依然是想等的,但是Object则不同了。

在阿里巴巴Java开发是手册的集合处理中需要强制遵循如下规则:

  1. 只要重写equals,就必须重写hashCode
  2. 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法
  3. 如果自定义对象做为Map的键,那么必须重写hashCode和equals
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值