前言
集合也可以成为容器,在Java语言的开发中有很高的实用度;所以集合中的一些行为以及一些特有的特性,很有必要知道;List和Set都是Colloection的
Collection 体系
常用的实现类
Collection中的方法:
int size(); //元素个数
boolean isEmpty(); //集合是否为空,如果此集合不包含元素,则返回 true 。
boolean contains(Object var1); //如果此集合包含指定的元素,则返回true
Iterator<E> iterator(); //返回此集合中的元素的迭代器。
Object[] toArray(); //返回一个包含此集合中所有元素的数组。
<T> T[] toArray(T[] var1); //返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。var1 - 要存储此集合的元素的数组,如果它足够大; 否则,为此目的分配相同运行时类型的新数组。
boolean add(E var1); //往集合中添加元素
boolean remove(Object var1); //从该集合中删除指定元素的单个实例(如果存在)
boolean containsAll(Collection<?> var1); //如果此集合包含指定 集合中的所有元素,则返回true。
boolean addAll(Collection<? extends E> var1); //将指定集合中的所有元素添加到此集合
boolean removeAll(Collection<?> var1); //删除指定集合中包含的所有此集合的元素
boolean retainAll(Collection<?> var1); //从该集合中删除所有不包含在指定集合中的元素。
void clear(); //从此集合中删除所有元素
由于Collection是接口所以不能直接new对象调用方法,可以以多态的方式使用这些方法;例如:Collection<String> c = new ArrayList(); c.add("aa11");
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
多态三要素:继承、 重写、 父类引用指向子类对象
List
Set
HashSet
HashSet原理(HashSet如何保证元素唯一性的原理)
1,我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率, 降低了使用equals()方法的次数
当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象
如果没有哈希值相同的对象就直接存入集合,如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存
2,将自定义类的对象存入HashSet去重复
去重复的条件:自定义类中必须重写hashCode()和equals()方法
hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)
equals(): 属性相同返回true, 属性不同返回false,返回false的时候存储
LinkedHashSet的概述和使用
底层是链表实现的,是set集合中唯一一个能保证怎么存就怎么取的集合对象。因为是HashSet的子类,所以也是保证元素唯一的,与HashSet的原理一样
原理:通过链表实现可以保证怎么存就怎么取 ,通过哈希算法保证元素唯一(保证元素为和HashSet原理一样)
TreeSet原理
TreeSet原理
特点
TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列
使用方式
a,自然顺序(Comparable)
TreeSet类的add()方法中会把存入的对象提升为Comparable类型
调用对象的compareTo()方法和集合中的对象比较
根据compareTo()方法返回的结果进行存储
public void test() {
TreeSet<Person> ts = new TreeSet<>();
ts.add(new Person("张三", 23));
ts.add(new Person("李四", 13));
ts.add(new Person("周七", 13));
ts.add(new Person("王五", 43));
ts.add(new Person("赵六", 33));
System.out.println(ts);
//比较长度,用以下代码排序,结果比较容易理解
// TreeSet<Person> ts = new TreeSet<>();
// ts.add(new Person("zhangsan", 23));
// ts.add(new Person("lisi", 13));
// ts.add(new Person("wangwu", 33));
// ts.add(new Person("zhaoliu", 43));
// ts.add(new Person("aaaa", 53));
//
// System.out.println(ts);
}
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
/*@Override
//按照年龄排序 注意:this.age代表要存入Person对象的age,o.age代表已经存入的Persion的age
public int compareTo(Person o) {
int num = this.age - o.age; //年龄是比较的主要条件
return num == 0 ? this.name.compareTo(o.name) : num; //姓名是比较的次要条件
}*/
/*@Override
//按照姓名排序 注意:this.name代表要存入Person对象的name,o.name代表已经存入的Persion的name
public int compareTo(Person o) {
int num = this.name.compareTo(o.name); //姓名是主要条件
return num == 0 ? this.age - o.age : num; //年龄是次要条件
}*/
/*
* aaa
* bbb
*/
//注意:this.name代表要存入Person对象的name,o.name代表已经存入的Persion的name
public int compareTo(Person o) {
int length = this.name.length() - o.name.length(); //比较长度为主要条件
int num = length == 0 ? this.name.compareTo(o.name) : length; //比较内容为次要条件
return num == 0 ? this.age - o.age : num; //比较年龄为次要条件
}
}
底层二叉树实现存储的过程
二叉树:首先有个根元素,然后再存入元素的时候和根元素比较,比根元素小存储在根元素左边,比根元素大存储在根元素右边,以此类推每个元素存入的时候都会和集合里面已存入的元素一一比较;
运行结果
按年龄排序:[Person [name=周七, age=13], Person [name=李四, age=13], Person [name=张三, age=23], Person [name=赵六, age=33], Person [name=王五, age=43]]
按照姓名排序:[Person [name=周七, age=13], Person [name=张三, age=23], Person [name=李四, age=13], Person [name=王五, age=43], Person [name=赵六, age=33]]
按照长度排序:[Person [name=aaaa, age=53], Person [name=lisi, age=13], Person [name=wangwu, age=33], Person [name=zhaoliu, age=43], Person [name=zhangsan, age=23]]
b,比较器顺序(Comparator)
创建TreeSet的时候可以制定 一个Comparator
如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
add()方法内部会自动调用Comparator接口中compare()方法排序
调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
public static void main(String[] args) {
//需求:将字符串按照长度排序
TreeSet<String> ts = new TreeSet<>(new CompareByLen()); //Comparator c = new CompareByLen();
ts.add("aaaaaaaa");
ts.add("z");
ts.add("wc");
ts.add("nba");
ts.add("cba");
System.out.println(ts);
}
class CompareByLen /*extends Object*/ implements Comparator<String> {
@Override
public int compare(String s1, String s2) { //按照字符串的长度比较
int num = s1.length() - s2.length(); //长度为主要条件
return num == 0 ? s1.compareTo(s2) : num; //内容为次要条件
}
}
底层通过二叉树存储的过程
执行结果:
[z, wc, cba, nba, aaaaaaaa]
c,两种方式的区别
TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
TreeSet如果传入Comparator, 就优先按照Comparator
HashSet 和 LinkedHashSet都是通过哈希算法保证元素唯一
TreeSet是通过元素实现Comparable<T> 接口中的compareTo方法保证元素唯一及对元素进行排序;
注意compareTo方法返回值的解释:
当compareTo方法返回0的时候集合中只有一个元素
当compareTo方法返回正数的时候集合会怎么存就怎么取
当compareTo方法返回负数的时候集合会倒序存储
遍历:
1,List
a.普通for循环, 使用get()逐个获取
b.调用iterator()方法得到Iterator, 使用hasNext()和next()方法
c.增强for循环, 只要可以使用Iterator的类都可以用
d.Vector集合可以使用Enumeration的hasMoreElements()和nextElement()方法
2,Set
a.调用iterator()方法得到Iterator, 使用hasNext()和next()方法
b.增强for循环, 只要可以使用Iterator的类都可以用
3,普通for循环,迭代器,增强for循环不可以在遍历的过程中删除