集合的分类及特点
Collection 1.List:是有序的(此处顺序是指按加入的元素先后顺序),可重复的,可以通过下标对集合进行遍历,修改;
-----1.ArrayList
ArrayList的底层是通过数组来实现的,所以相较于LinkedList,ArrayList查找的效率比较高(没有那么多指针,数组要插入的 话,一要扩容,二要挪动数组中的元素,费时费力);
-----2.LinkedList
LinkedList的底层是通过链表来实现的,所以相较于ArrayList, LinkedList插入的效率比较高(只要改变指针就行);
学过数据结构后纠正一下,LinkedList底层是双向链表,准确的说是双向对列,相较于单向,查找速度快了一倍(两头开始查找)!
-----3.Vector
底层和ArrayList相同,但是Vector加了线程锁,解决了线程安全问题,但是同样的降低了效率;
2.Set:无序的,不可重复的;
------1.HashSet
不可重复性:
HashSet<String> hashSet = new HashSet<>();
hashSet.add("abc");
hashSet.add("abc");
hashSet.add("def");
for (String str:hashSet){
System.out.println(str);
}
结果为abc和def,因为重复了就只取一个;
在这儿补充一点,List和HashSet都可一存放null,但HashSet只能存放一个,TreeSet存放null不会报错,但一遍历就会报空指针异常
然后这儿有个坑:
HashSet<Person> hashSet = new HashSet<>();
hashSet.add(new Person(18,"小王"));
hashSet.add(new Person(18,"小王"));
hashSet.add(new Person(28,"小许"));
for (Person person:hashSet){
System.out.println(person);
}
结果输出三个对象,因为两个小王虽然内容相同,但是是两个不同的对象(地址是不同滴),而且输出是无序的,但每次输出顺序相同.,同一段代码重复运行好几次会发现每次顺序都相同,这说明Set集合输出遵循某种排序方法, 就是hashcode的值.
关于HashSet中HashCode的补充
Set集合的输出顺序是按HashCode排序的,但并不是直接按照HashCode的大小进行排序的.
------2.TreeSet
TreeSet和HashSet的不同之处在于TreeSet对元素进行了排序(但还是不能使用index进行操作)
Set<String> set = new TreeSet<>();
set.add("f");
set.add("d");
set.add("a");
set.add("b");
for (String s:set){
System.out.println(s);
}
结果为abdf,进行了排序.
但遗憾的是TreeSet只能对int,String等类型进行比较排序,如果放入自定义的类进行输出就会报错.因为对象不能比较大小.
集合的遍历,修改,删除等操作
List集合有序而Set集合无序,意味着List可以操作下标而Set不行!所以在对集合进行遍历或者修改删除的时候应该特别注意.
对List集合遍历,删除有两种方法,即使用迭代器和使用下标
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(3);
list.add(5);
list.add(6);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
if (iterator.next()==3){
iterator.remove()
//list.remove(3);
}
}
System.out.println(list);
}
首先要注意的时迭代器也是有类型的,如果没有填类型,那么默认为Obj类型,这样一来
if (iterator.next()==(Integer)3){ }
需要强转,因为iterator.next的值时obj而不是Integer;//此处强烈注意,掉过两次坑,丢人!
所以迭代器请加泛型谢谢 !
所以迭代器请加泛型谢谢 !
所以迭代器请加泛型谢谢 !
迭代器不操作下标,所以迭代器的方法都不带下标.
在迭代过程中请勿直接操作集合元素,不如被注释掉的list.remove,他打乱了迭代器,会让迭代器报错.
另外迭代器比较坑爹的两个方法,一个叫next,一个叫remove
iterator.next:返回迭代器的下一个元素
iterator.remove:很坑爹,作用时删除返回的上一个元素而不是下一个元素,跟next放一起我怀疑是☀公司专门恶心人的!
由此带来的一个隐患就是如果你一上来就使用iterator.remove就会报错:java.lang.IllegalStateException
当然这不是最恶心的,实际操作发现
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(3);
list.add(5);
list.add(6);
Iterator<Integer> iterator = list.iterator();
iterator.next();
iterator.remove();
iterator.remove();
System.out.println(list);
}
上诉代码照样报java.lang.IllegalStateException错误,原因是next返回的元素被第一个remove删除了,第二个remove找不到上一个返回的元素,疯狂报错
使用iterator.remove()之前必须使用iterator.next();
使用iterator.remove()之前必须使用iterator.next();
使用iterator.remove()之前必须使用iterator.next();
第二种对集合操作的方法是使用循环,此处需注意的是:虽然加强for循环也是循环,但for each不能操作下标,其根本原因是for each其实底层也是迭代器!!!!!
对于Set类型集合,因为是无序的无法操作下标,就只能用迭代器来对其操作,当然包括for each.
经实验发现:
public class LinkedDemo {
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(2);
list.add(2);
list.add(1);
list.add(2);
list.addFirst(666);
list.addLast(888);
list.remove((Integer) 1);
System.out.println(list);
}
}
当集合为integer类型,要删除时按元素而不是按下标时:
使用list.remove((Integer)1);
强转后删除
拓展:比较器
什么是比较器:接口comparable和接口compartor的实现类即为比较器.
1.1内部比较器,实现comparable接口.我通过Car类去实现comparable接口(comparable接口中只有compareTo一个方法)成功的实现了对ArrayList<Person>集合的排序.
public class Person implements Comparable{
private String name;
private int age;
private double money;
public Person(String name, int age, double money) {
this.name = name;
this.age = age;
this.money = money;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", money=" + money +
'}';
}
@Override
public int compareTo(Object o) {
Person p = (Person)o;
// return age-p.age; 返回两个对象age的差值
return money>p.money?1:money==p.money?0:-1; //大于形参money返回1,等于形参返回0,小于返回-1
// return name.compareTo(p.name);//String类型用compareTo比较
}
}
实验发现,返回属性差值,或者直接指定返回1,0,-1都行.
之后
public class Collection {
public static void main(String[] args) {
List<Person> pList = new ArrayList<Person>();
pList.add(new Person("aa",88,899));
pList.add(new Person("bb",9,10000));
pList.add( new Person("cc",100,666));
System.out.println(pList);
}
}
输出结果为
[Person{name='cc', age=100, money=666.0}, Person{name='aa', age=88, money=899.0}, Person{name='bb', age=9, money=10000.0}]
结果为按照Person类里面内部比较器中按money属性升序排列.Person类里面比较器比什么就按什么排序.
public int compareTo(Object o) {
Person p = (Person)o;
int i = age - p.getAge();
int j = name.compareTo(p.getName());
return i==0?j:i;
}
内部比较器还可以比较两个值来进行排序,即如上代码:先比较age,如果age相同,再按name排序
内部比较器在List和Set接口都可以用内部比较器来排序,
外部比较器需要配合Collections.sort()方法使用,但这个方法不能放Set集合
1.2外部比较器.
首先是一个Person类
public class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
然后写外部比较器
public class PersonCompartor implements Comparator {
@Override
public int compare(Object o1, Object o2) {
Person p1 = (Person)o1;
Person p2 = (Person)o2;
/*通过age比较,p1比p2大就返回1,相等就返回0,小于就返回-1*/
int result = p1.age>p2.age?1:(p1.age==p2.age)?0:-1;
return result ;
}
}
这边使用了List集合,可直接通过Collections.sort方法进行排序(小补充:Collection是集合框架的父接口,Collections是一个包装类,里面有很多关于集合的常用方法)
List<Person>list = new ArrayList<>();
list.add(new Person("小齐",25));
list.add(new Person("小李",20));
list.add(new Person("小王",15));
list.add(new Person("小周",48));
Collections.sort(list,new PersonCompartor());
System.out.println(list);
输出结果即为:
[Person{name='小王', age=15}, Person{name='小李', age=20}, Person{name='小齐', age=25}, Person{name='小周', age=48}]
想改变升序或者降序只需改变外部比较器即可,当然可以写两个不同的比较器升序降序随你选;
1.3无论是实现Comparator还是Comparable接口,都应该使用返回-1,0,1的方式来实现接口而不是两个值相减(在为数值类型的情况下)
因为如果两个小数相减,可能会得到一个很小的数值,但是比较器的返回值是int类型,故需要向上取整然后强转成int类型,太麻烦了,故直接返回-1,0,1比较简单.
匿名内部类应用在比较器中的应用
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("小齐",25));
list.add(new Person("小李",20));
list.add(new Person("小王",15));
list.add(new Person("小周",48));
Collections.sort(list, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
Person p1 = (Person)o1;
Person p2 = (Person)o2;
/*按年龄顺序降序排列*/
return p1.getAge()>p2.getAge()?-1:p1.getAge()==p2.getAge()?0:1;
}
});
System.out.println(list);
}
相较于之前先建立Compator的子类,然后在Collection.sort()中new子类的对象,直接在此处建立内部类更加的简便了.
Set类型的集合使用外部比较器进行排序(集合之间的互相转化!!!)
public static void main(String[] args) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
lhs.add(1);
lhs.add(5);
lhs.add(19);
lhs.add(8);
lhs.add(10);
System.out.println(lhs);
ArrayList<Integer> List = new ArrayList<>(lhs);
Collections.sort(List);
}
可以通过集合的有参构造方法方法转换集合的类型
比如ArrayList<Integer> List = new ArrayList<>(lhs);
这一行代码即将lhs(LinkedHashSet)集合转变成了ArrayList集合
然后就可以用Collections.sort进行排序了
同理,也可以将List的实现类集合转变成Set集合.
额外补充LinkedHashSet
LinkedHashSet的底层是链表,他与HashSet的不同之处在于,LinkedHashSet是有序的(这儿的有序并不是与TreeSet一样经过排序之后输出,而是与List集合一样,遵循输出顺序按照add顺序)
public static void main(String[] args) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
lhs.add(1);
lhs.add(5);
lhs.add(19);
lhs.add(8);
lhs.add(10);
System.out.println(lhs);
}
输出的顺序为add顺序