# 原创,转载请先留言
1.集合的由来
数组的长度是固定的,当需要增加或减少元素时需要对数组重新定义,太麻烦了。java内部给我们提供了集合类,能存储任意对象,长度可以改变的,随着元素的增加而增加,随着元素的减少而减少。
2.数组和集合的区别
(1)数组既可以存储基本数据类型,又可以存储引用数据类型(存储基本数据类型时存储的是值,存储引用数据类型的是地址值)。集合只能存储引用数据类型,但是实际用的时候可以存储基本数据类型,为什么呢?(是因为在存储的时候进行了自动装箱,例如 int 自动装箱为integer)
(2)数组长度是固定的,不能自动增长。集合的长度是可变的,根据元素的增加而增长。
3.数组和集合什么时候用
(1)如果元素个数是固定的推荐用数组(为什么不一直用集合呢?举个例子,你要存储固定的50个元素,用数组直接划分50个空间就可以存储了。但是用集合的话,例如,某些集合的底层原理是使用数组做的[ArrayList],他会先创建10个空间,不够再增长到1.5倍,到15,再不够再增长,效率低。)
(2)如果元素个数不是固定的推荐用集合
4.集合继承体系
看API文档可以知道Collection是接口。
而且还有两大子类接口List和Set。
List和Set下会有各种实现类。如下图:
5.各个接口或类常用方法
-
Collection接口
Collection是一个接口,所以不能实例化,即不能创建对象。
Collection有几个常见的接口方法,可以体验一下:
boolean add(E e) 添加元素
boolean remove(Object o) 删除元素
void clear() 清除集合
boolean contains(Object o) 是否包含
boolean isEmpty() 是否为空集合
int size() 集合元素的个数
object[] toArray 把集合转换为对象数组
boolean addAll(Collection<? extends E> c) 把另一个集合的元素添加到该集合
boolean removeAll(Collection<?> c) 删除指定集合中包含的所有此集合的元素
boolean containsAll(Collection<?> c) 如果此集合包含指定 集合中的所有元素,则返回true
boolean retainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(注意这个返回值比较反人类,后面示例2验证)
Iterator<E> iterator() 返回集合的迭代器
示例1:(toArray方法)
public static void main(String[] args) { Collection c = new ArrayList(); c.add(new Students("张三",13)); // 这里其实实现了自动装箱,Object o = new Students("张三",13),因为add的参数是Object对象 c.add(new Students("李四",14)); c.add(new Students("王五",15)); //遍历集合,先把集合转化为数组,然后遍历 Object[] arr= c.toArray(); // 注意返回值是Object对象,不是Students对象 for (int i = 0;i < arr.length;i++){ // 如果要用到子类的特有的属性或者方法,比较向下转型为子类对象 Students s = (Students) arr[i]; System.out.println(s.getName() + "---" + s.getAge()); // 当然,你也可以重写toString方法直接输出,就不用向下转型了 } }
示例2:(retainAll方法)
public static void main(String[] args) { Collection c = new ArrayList(); c.add(100); c.add(200); c.add(300); Collection c2 = new ArrayList(); c2.add(100); c2.add(200); c2.add(300); // c2.add(400); Boolean flag = c.retainAll(c2); // 返回值有点古怪!c如果变化了,就是true。如果没变化就是false System.out.println(c); System.out.println(flag); }
-
List接口
List是一个接口,不能实例化。
List有几个Collection没有的接口方法。
void add(int index,E element) 在指定索引添加元素
E remove(int index) 删除指定索引的元素
E get(int index) 获取指定索引的元素
E set(int index,E element) 修改指定索引的值
易错点:(remove方法)
public static void main(String[] args) { List l = new ArrayList(); l.add(100); l.add(200); l.add(300); /*IndexOutOfBoundsException.注意,虽然Colletion有这个remove(Object o)接口方法, 但是remove([如果是int类型])不会自动装包,所以输入数字的时候默认是index。但是如果是其他类型是可以的。*/ l.remove(100); System.out.println(l); }
-
ArrayList、LinkedList、Vetor类
ArrayList类底层是数组实现的。线程不安全,效率高。查询修改快,增删慢。
Vetor类底层是数组实现的。线程安全,效率低。查询修改快,增删慢。(基本已经被ArrayList取代了)
LinkList类底层是链表实现的。线程不安全,效率高。增删快,查询修改慢。
* 如果增删多用LinkedList,如果查询修改多用ArrayList
-
Set接口
打开API文档看了下,set接口的接口方法基本上和Collection的接口一样,所以可以set接口方法的使用可以参考上面的Collection接口方法。
-
HashSet类
HashSet类是Set接口的实现类。HashSet只能存储不同的元素、
boolean add(E e) 将指定的元素添加到此集合(如果尚未存在)
但是add对象的时候出现了问题:
public static void main(String[] args) { HashSet<Students> hs = new HashSet<>(); hs.add(new Students("张三",13)); hs.add(new Students("张三",13)); hs.add(new Students("李四",14)); hs.add(new Students("李四",14)); hs.add(new Students("李四",14)); for (Students s:hs) { System.out.println(s); } } // 输出结果: Students{name='张三', age=13} Students{name='李四', age=14} Students{name='李四', age=14} Students{name='张三', age=13} Students{name='李四', age=14}
如果我们把同名与同年龄的看做同一个对象,希望输出结果只有张三,李四呢?
根据ArrayList遇到这种情况的时候,第一时间想到了equals方法,重写equals方法试试:
@Override
public boolean equals(Object obj) {
Students s = (Students)obj;
return this.name.equals(s.name) && this.age == s.age;
}
运行,发现并不可行,输出结果还是那个。问题出在哪里?
其实HashSet的判断方式是这样的,先判断hashCode,如果hashCode一样,才会判断equals方法。如果hashCode不一样,那么直接认为他们是不一样的。
我们先让Students对象的hashCode都是一个数,尝试一下:
@Override
public int hashCode() {
return 10;
}
发现结果符合我们的预期了,返回的是张三、李四两个对象。是因为大家的hashCode都一样,然后就运行equals方法进行比较。
但是这样的话,hashCode的方法就完全没有意义了。而且每次都会运行equals方法,效率很低。设想这么一个场景,每个对象都是不一样的,然而返回的哈希值一样,然后每一个都运行equals方法进行比较,效率十分低。
最理想的状态就是运行hashCode方法时相同的对象就是相同的值,不同的对象就是不同的值。这样才是最高效率的。而equals方法只是一个后备,万一不同的对象算出来的哈希值一样,equals方法才用的上。所以必须hashCode方法与equals方法都必须重写!
比较重要的是要想办法重写一个漂亮的hashCode方法。但是IDE已经帮我们完成了这个- -
IDEA下Alt+Insert选择equals and hashCode就行了。
-
LinkedHashSet类(上面的图没有画出来)
LinkedHashSet是HashSet的子类,用法基本和HashSet一样。LinkedHashSet底层是用链表与哈希表实现。LinkedHashSet最大的特点就是它是Set族中唯一一个能怎么存怎么取的。
-
TreeSet集合
TreeSet集合可以对元素进行排序,当然也可以保证元素的唯一。
关于TreeSet要注意的内容比较多,可以见我的另一篇博文: