Collection集合
集合是:存储对象的容器,存储对象的对象(elements)。在数组中也可以存储对象,可代替数组,而用Collection集合存储对象,可以使对象拥有更多的特点和功能。 特点是:容器的工具类,定义了对多个对象进行操作的方法。
public interface Collection<E> extends Iterable<E>
Collection接口的子类集合有些是允许有重复元素,有些却没有。JDK不提供任何实现类直接实现此接口。Collection接口有很多抽象方法,是它的子接口或者实现类共性的标准。 Collection中有20个方法:
List接口集合
List接口的特点:有序,有下标,元素可以重复。 从List接口的特点,可以猜出List接口中定了一些抽象方法。
public interface List<E> extends Collection <E>
List接口中有41个方法
-
void add(int index,Object o);//在index的位置上插入对象o。
-
boolean addAll(int index,Collection c);//将一个集合中的元素添加到此集合中的index位置。
-
Object get(int index);//返回指定位置的元素。
-
List subList(int fromIndex,int toIndex);//返回它们之间的集合元素。
List接口---ArrayList实现类(重点)
ArrayList实现类遵循Collection和List接口的标准,所以ArrayList实现类具备有序、有下标、元素可以重复的3种特点,底层是数组结构实现,查询快,增删慢。
注:当在中间插入元素时,后面的每个元素需要往后按顺序移动。
public class ArrayList<E> extends AbstractList <E> implements List <E>, RandomAccess , Cloneable , Serializable
List
接口的可调整大小数组实现。实现所有可选列表操作,并允许所有元素,包括 null
。除了实现 List
接口之外,此类还提供了一些方法来操纵内部用于存储列表的数组的大小。 (这个类大致等同于 Vector
,除了它是不同步的,所以线程不安全,但是运行效率快)。
ArrayList的构造方法:
ArrayList() 构造一个初始容量为 10 的空列表。
ArrayList(int initialCapacity) 构造一个具有指定初始容量的空列表。
ArrayList(Collection<? extends E> c) 构造一个包含指定集合元素的列表,按照集合迭代器返回元素的顺序。
注:ArrayList实现类底层是一个数组;数组结构实现,查询快、增删慢。JDK6初始容量为10,JDK7初始化容量改为0。思考:为什么会改为0?答:一开始初始化容量为10的话,如果不使用这么多容量会造成资源浪费,初始化改为0时,只需添加一个元素会自动+1。
List接口---Vector实现类
Vector实现类与ArrayList实现类差不多时一样的,同样具备有序、有下标、元素可以重复三种特点,底层是数组结构实现,查询快,增删慢。
public class Vector<E> extends AbstractList <E> implements List <E>, RandomAccess , Cloneable , Serializable
(重点)Vector类与ArrayList类区别在于:Vector类是线程安全的,运行效率慢;而ArrayList类是不安全的,运行速率快。如果不考虑线程安全问题,建议使用ArrayList代替Vector。
List接口---LinkedList实现类
LinkedList实现类具备有序、有下标、元素可以重复三种特点,底层是链表结构实现,查询慢,增删快。
public class LinkedList<E> extends AbstractSequentialList <E> implements List <E>, Deque <E>, Cloneable , Serializable
所有操作的执行都符合双向链表的预期。索引到列表中的操作将从开头或结尾遍历列表,以更接近指定索引的为准。
注:在中间插入元素时,无需移动元素,直接开辟空间让前面的元素指向该空间。
泛型集合
泛型集合
泛型集合的概念:参数化类型、类型安全的集合,强制集合的元素类型一致。一个容器的泛型是什么引用类型,就只能放入相对应的数据类型。
特点: 1、编译时即可检查,而非运行时抛出异常。 2、访问时,不必类型转换(拆箱)。 3、不同泛型之间引用不能相互赋值,泛型集合不存在多态。
泛型集合的由来:早期使用Object的引用来实现参数的“任意化”,而该特点的缺陷是要做显示的强制类型转换。对于强制转换类型错误的情况,编译器可能不提示错误,而在运行时才出现异常,这是一个类型的安全隐患,所以泛型就是为了阻止这个安全问题产生的。
泛型的好处:更好的安全性、可读性,省去强转类型的麻烦。
程序正常运行的重要目标是把bug(异常)扼杀在摇篮里,能在写代码的时候处理异常,就不要等到程序运行的时候。
不加泛型时,例如:
public class TestGenericityArrayList{ public static void main(String[] args){ List list = new ArrayList(); list.add('a'); list.add(20D); list.add(true); list.add(20); int sum = 0; for(int i=0;i<list.size();i++){ // sum += list.get(i);//error,不同类型之间不能做运算 } System.out.printin(sum); } }
加泛型时,例如:
public class TestGenericityArrayList { public static void main(String[] args) { List<Double> list = new ArrayList<Double>(); // list.add('a');//error,加了泛型之后,需要遵循泛型的定义标准,不能往集合添加指定泛型以外的类型 list.add(20D); list.add(50D); list.add(200D); list.add(150D); // list.add(true); // list.add(20); int sum = 0; for(int i=0;i<list.size();i++){ sum += list.get(i); } System.out.println(sum);//420 } }
注:<>里面放的必须是引用数据类型,例如:自定义的类,或者包装类等;泛型集合可以添加类和子类的对象
泛型的注意事项:
泛型的前后<>的引用数据类型必须一致,或者后面的那个<>不写;数组的类型前后也要一致。
//泛型 ArrayList<Integer> list = new ArrayList<Integer>(); List<Double> list = new Vector<>(); //数组 int[] arr = new int[]{}; //错误示范 List<Object> list = new ArrayList<Student>();//Error,泛型不存在多态 int[] arr = new String[]{};
Set接口集合
Set接口的特点:无序、无下标、元素不可重复。 从Set接口的特点来看,List接口中有一些定义的包含有下标(index)的方法是没有的。
public interface Set<E> extends Collection <E>
Set接口中有29个方法:
Set---HashSet实现类
HashSet类实现了Set接口,则HashSet类具备无序、无下标、元素不可重复三种特点。
public interface Set<E> extends Collection <E>
不包含重复元素的集合。更正式地说,集合不包含满足 e1.equals(e2) 的一对元素 e1 和 e2,并且最多包含一个空元素。顾名思义,这个接口模拟数学set抽象。因为没有下标,只能使用forEach循环遍历。
构造方法:
HashSet() 构造一个新的空集;后备 HashMap 实例具有默认初始容量 (16) 和负载因子 (0.75)。
HashSet(int initialCapacity) 构造一个新的空集;后备 HashMap 实例具有指定的初始容量和默认负载系数 (0.75)。
HashSet(int initialCapacity, float loadFactor) 构造一个新的空集;后备 HashMap 实例具有指定的初始容量和指定的负载因子。
HashSet(Collection<? extends E> c) 构造一个包含指定集合中的元素的新集合。
【复习:】Object类的hashCode方法
public int hashCode(){} 作用:返回该对象的十进制的哈希码值。 哈希算法根据对象的地址或者字符串或者数字计算出来的int数值。 哈希码值并不唯一,但是可以保证相同对象返回相同的哈希码值,尽量保证不同的对象返回不同的哈希码值。
【思考:】如何保证HashSet集合的元素不重复??
HashSet集合是首先通过判断对象的hashCode()哈希码值是否相同,如果哈希码值相同,则会自动调用equals()来判断是否为同一对象(可以重写equals方法判断认为内容相同也是相同元素);如果哈希码值不相同,则可插入该元素。
例如:
public class Test_HashSet { public static void main(String[] args) { HashSet<Student> students = new HashSet<Student>(); Student s1 = new Student("chen",21,"男",98D); Student s2 = new Student("ding",19,"女",99D); Student s3 = new Student("wang",15,"男",92D); Student s4 = new Student("lily",20,"女",93D); Student s5 = new Student("chen",21,"男",98D); students.add(s1); students.add(s2); students.add(s3); students.add(s4); students.add(s1); //地址相同(hashCode返回的值相同),被认定为重复元素。 students.add(s5); //地址不同、内容相同,被认定为重复元素 for(Student stu : students){ System.out.println(stu); } } } class Student{ String name; Integer age; String sex; Double score; public Student() {} public Student(String name, Integer age, String sex, Double score) { this.name = name; this.age = age; this.sex = sex; this.score = score; } @Override public String toString() { return "Student [" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", score=" + score + ']' + "\t" + this.hashCode(); } @Override public int hashCode() { return this.name.hashCode() + this.age + this.sex.hashCode() + this.score.hashCode(); } //当返回的hashCode值与HashSet里的某一个值相同时,会自动调用equals方法再判断 @Override public boolean equals(Object o) { System.out.println("---equals Executed---"); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return Objects.equals(name, student.name) && Objects.equals(age, student.age) && Objects.equals(sex, student.sex) && Objects.equals(score, student.score); } }
Set---LinkedHashSet实现类
上面的HashSet的特点是无下标、无序、元素不可重复。当需要一个有序但不可重复的集合时,可以用LinkedHashSet类。LinkedHashSet类比HashSet多了链表结构,与其无差多少。
LinkedHashSet实现类的特点: 1、元素不可重复 2、没有下标 3、有序(按插入的顺序排列或者自然排序)
LinkedHashSet类的底层是哈希表+链表,与HashSet相比多了链表,也就是有了链表才能保证有序。
public class LinkedHashSet<E> extends HashSet <E> implements Set <E>, Cloneable , Serializable
构造方法:
LinkedHashSet()使用默认初始容量 (16) 和加载因子 (0.75) 构造一个新的空链接哈希集。
LinkedHashSet(int initialCapacity)使用指定的初始容量和默认加载因子 (0.75) 构造一个新的空链接哈希集。
LinkedHashSet(int initialCapacity, float loadFactor)使用指定的初始容量和加载因子构造一个新的空链接哈希集。
LinkedHashSet(Collection<? extends E> c)使用与指定集合相同的元素构造一个新的链接哈希集。
Set---TreeSet实现类
特点: 1、元素不可重复 2、无下标 3、对元素自动排序
TreeSet集合中的元素必须是可以排序的。
public class TreeSet<E> extends AbstractSet <E> implements NavigableSet <E>, Cloneable , Serializable
构造方法:
TreeSet()构造一个新的空树集,根据其元素的自然顺序进行排序。
TreeSet(Collection<? extends E> c)构造一个新的树集,包含指定集合中的元素,按照自然排序它的元素。
TreeSet(Comparator<? super E> comparator)构造一个新的空树集,根据指定的比较器排序。
TreeSet(SortedSet<E> s)构造一个包含相同元素并使用与指定排序集相同顺序的新树集。
【思考:】TreeSet集合中是如何对元素进行排序的?
【答:】TreeSet实现了SortedSet接口,所以集合中的元素必须是可以进行排序的,则会对集合中的元素自动排序。也可以通过compareTo方法指定排序规则。如果是自定义的类对象元素,类必须实现Comparable接口,当compareTo返回的是0,则会被认定为重复元素。
例如:
public class TestTreeSet2 { public static void main(String[] args) { TreeSet<Student> ts = new TreeSet<Student>(); Student stu1 = new Student("wang",18,"男",94D); Student stu2 = new Student("wang",18,"女",94D); Student stu3 = new Student("ding",19,"女",99D); Student stu4 = new Student("chen",22,"男",97D); ts.add(stu1); ts.add(stu2); ts.add(stu3); ts.add(stu4); for(Student stu : ts){ System.out.println(stu); } } } class Student implements Comparable<Student>{//必须实现Comparable接口中的抽象方compareTo String name; int age; String sex; double score; public Student(String name, int age, String sex, double score) { this.name = name; this.age = age; this.sex = sex; this.score = score; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", score=" + score + '}'; } @Override public int compareTo(Student student) {//compareTo等于0时,TreeSet会被认定为重复元素 //年龄降序 if(this.age > student.age) { return -1; }else if(this.age < student.age){ return 1; }else{ //如果年龄相等时,以分数做降序排序 if(this.score > student.score){ return -1; }else if(this.score < student.score){ return 1; }else{ //如果年龄和分数都相等,以姓名做升序排序 if(this.name.compareTo(student.name) > 0){//True为升序 //System.out.println(this.name.compareTo(student.name)); //System.out.println("被执行"); return 1; }else if(this.name.compareTo(student.name) < 0){//True为降序 return -1; }else { //如果年龄、分数、姓名都相同,则判断性别是否为相同,如果相同会被认为同一元素或对象 return this.sex.compareTo(student.sex); } } } } }