目录
主要内容:
1、Collection接口
2、List接口
3、ArraList类
4、Vector类
5、LinkedList类
6、Set接口
一、List接口:
1.1 List集合特有功能:
void add(E e)
void add(int index,E e) 向指定位置插入元素
E remove(int index)移除列表中指定位置的元素(可选操作)。将所有的后续元素向左移动(将其索引减 1)。返回从列表中移除的元素。
E get(int index):返回列表中指定位置的元素。
E set(int index,E element)用指定元素替换列表中指定位置的元素(可选操作)。
public class ListDemo01 { public static void main(String[] args) { List list = new ArrayList(); list.add("AAA"); list.add("BBB"); list.add("CCC"); list.add("DDD"); print(list); list.add(2, "EEE"); print(list); // list.remove(2); list.set(2, "eee"); print2(list); }
public static void print(List list) { Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); } }
public static void print2(List list) { for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } }
|
1.2 ArrayList类:
数组实现。长度可变。
1.3 LinkedList:
数组:存储是连续的0,查询、修改快。增加删除慢。
链表集合。在内存中每个元素存储不连续,查询慢、修改慢。增加快、删除快。
1.4 Vector类:
Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。
特有功能(与ArrayList和LinkedList不同)
public void addElement(E obj):将指定的组件添加到此向量的末尾,将其大小增加 1。如果向量的大小比容量大,则增大其容量。
public E elementAt(int index):返回指定索引处的组件
public Enumeration<E> elements():返回此向量的组件的枚举。返回的 Enumeration 对象将生成此向量中的所有项。生成的第一项为索引 0 处的项,然后是索引 1 处的项,依此类推。
Enumeration:枚举。
public class VectorDemo { public static void main(String[] args) { Vector vector = new Vector(); vector.addElement("A"); vector.addElement("B"); // System.out.println(vector.elementAt(0));
// 遍历(迭代) Enumeration enumeration = vector.elements(); // 枚举 while (enumeration.hasMoreElements()) { System.out.println(enumeration.nextElement()); } } } |
1.5 List接口的三个实现类:
1、三个实现类的特点:
ArrayList:底层数据结构是数组,查改快,增删慢。
线程不安全,效率高。
Vector:底层数据结构是数组,查改快,增删慢。
线程安全,效率低。
Vector相对ArrayList查询慢。增删慢。
LinkedList:底层数据结构是链表,查改慢,增删快。
线程不安全,效率高。
2、ArrayList和Vector底层数据结构都是数组。
3、三个实现类的选择:
查改操作比较多,推荐使用ArrayList
增删操作比较多,推荐LinkedList
如果增删查改都多,推荐使用ArrayList
二、泛型:
2.1 泛型的概述:
JDK1.5,允许在编写集合代码时,先限制集合的处理类型,把原来程序运行时可能发生的问题,转变成编译时问题,可以有效及时解决问题,提高程序的可读性和稳定性。
public class GenericDemo01 { public static void main(String[] args) { List list = new ArrayList(); list.add(100); list.add("Hello"); list.add(250);
String[] arr = new String[list.size()]; for (int i = 0; i < list.size(); i++) { // arr[i] = list.get(i); } System.out.println(list.get(1)); } } |
public class GenericDemo02 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); // list.add(100); list.add("Hello"); // list.add(250);
String[] arr = new String[list.size()]; for (int i = 0; i < list.size(); i++) { arr[i] = list.get(i); } System.out.println(list.get(1)); } } |
2.2 泛型的专业术语:
以ArrayList<E>为例:
<>念为typeof,实际上就是集合的元素类型
E称为参数类型(元素类型)
ArrayList<String>中String就是元素类型
整个ArrayList<E>被称为泛型类型。
2.3 泛型擦除:
泛型只在编译时期有效,编译后字节码文件中会把泛型类型中元素类型去掉,不存在有效的泛型信息。
public class GenericDemo03 { public static void main(String[] args) {
}
// 泛型擦除 public void save(List<Employee> employees) {
}
public void save(List<Dept> depts) {
} } |
2.4 泛型写法:
public class GenericDemo04 { public static void main(String[] args) { // 声明泛型集合,集合两边类型必须保持一致 List<Object> list1 = new ArrayList<Object>(); List<Integer> list2 = new ArrayList<Integer>();
List<String> list3 = new ArrayList(); List list4 = new ArrayList<String>();
//错误:泛型的类型必须是引用类型,不能是基本类型。 List<char> list5 = new ArrayList<char>(); } } |
2.5 泛型方法/类/接口:
作用:设计公用的类和方法,对各种业务提取公共部分取实现,使程序更加灵活。
1、泛型方法:
public class GenericDemo05 { public <T> T save(List<T> list) { System.out.println("批量保存对象集合!"); return null; }
public static void main(String[] args) { // 使用泛型方法,由具体调用的时候,才正式确定泛型的类型 // 批量保存Employee List<Employee> list = new ArrayList<Employee>(); new GenericDemo05().save(list);
// 批量保存部门: List<Dept> depts = new ArrayList<Dept>(); new GenericDemo05().save(depts); } } |
2、泛型类:
public class GenericDemo06Test { public static void main(String[] args) { GenericDemo06<Employee> demo = new GenericDemo06<Employee>(); demo.save(new Employee()); } } |
public class GenericDemo06<T> { // 定义泛型方法 public void save(T t) { System.out.println("保存对象t"); } } |
3、泛型接口:
public interface BaseDao<T> { // 增删改查C-create,R-read,U-update,D-delete void create(T t);
void delete(int id);
void update(T t);
void get(int id);
List<T> listAll(); } |
泛型接口类型的确定:在调用具体的实现类中方法时确定。
2.6 泛型关键字:
? 指定只是接受值
extends 元素的类型必须是继承自指定的类,本身也可以。【上限】
super 元素的类型必须是指定类的父类(或超类),本身也可以。【下限】
1、关键字?
public class GenericDemo07 { public void save(List<?> depts) { // 传入的list集合只能读取、迭代,不能修改里面元素值。 depts.set(1, "XXX"); for (int i = 0; i < depts.size(); i++) { System.out.println(depts.get(i)); } }
public static void main(String[] args) { List<String> depts = new ArrayList<String>(); depts.add("测试部"); depts.add("研发部"); new GenericDemo07().save(depts); } } |
2、关键字extends:
public class GenericDemo08 { public static void main(String[] args) { List<Double> list1 = new ArrayList<Double>(); List<String> list2 = new ArrayList<String>(); // 不能作为save方法的参数
new GenericDemo08().save(list1); }
public void save(List<? extends Number> list) {
} } |
3、关键字super:
public class GenericDemo09 { public void save(List<? super String> list) {
}
public static void main(String[] args) { List<Object> list1 = new ArrayList<Object>(); new GenericDemo09().save(list1);
List<String> list2 = new ArrayList<String>(); new GenericDemo09().save(list2); } } |
三、HashSet类:
3.1 HashSet概述:
此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。
public boolean add(E e)
如果此 set 中尚未包含指定元素,则添加指定元素。更确切地讲,如果此 set 没有包含满足 (e==null ? e2==null : e.equals(e2)) 的元素 e2,则向此 set 添加指定的元素 e。如果此 set 已包含该元素,则该调用不更改 set 并返回 false。
public class HashSetDemo01 { public static void main(String[] args) { Set<String> set = new HashSet<String>(); boolean b1 = set.add("A"); boolean b2 = set.add("A"); System.out.println(b1); System.out.println(b2); // 遍历 for (String s : set) { // 增强for循环 System.out.println(s); }
Set<Person> persons = new HashSet<Person>(); persons.add(new Person("发生")); persons.add(new Person("有根")); persons.add(new Person("有根")); // 没有重写equals方法 for (Person person : persons) { System.out.println(person); } } }
|
HashSet存储自定义对象如何保证元素的唯一性——必须要重写equals方法和hashCode方法。
3.2 HashSet保证元素唯一性的原理:
1、HashSet原理:
我们使用Set集合都是需要去掉重复元素,如果在存储的时候逐个使用equals方法比较,效率低,哈希算法提高去重的效率,降低使用equals方法的次数。
当HashSet调add方法存储对象时,先调用对象的hashCode方法得到一个hash值,然后在集合中查找是否有相同哈希值的对象:
如果没有hash值相同的对象,就直接存入集合;
如果有hash值相同的对象,就和hash值相同的对象逐个进行equals比较,比较结果是false就存入集合,如果是true就不存。
2、将自定义类型的对象存入HashSet去重:
类中必须重写hashCode和equals方法。
hashCode方法:属性值相同的对象返回值必须相同。
equals方法:属性值相同返回true,属性值不相同返回false。返回false时应该存储。
3.3 HashSet面试题:
- 需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并且把随机数打印到控制台。
public class HashSetDemo02 { // 需求:编写一个程序,获取10个1-20之间的随机数,要求随机数不能重复,并且把随机数打印到控制台。 public static void main(String[] args) { Set<Integer> set = new HashSet<Integer>(); Random random = new Random(); // Math.random() double:[0.0,1.0) // Random nextInt(int seed) while (set.size() < 10) { // 1,2,3,....,20 set.add(random.nextInt(20) + 1); }
for (Integer n : set) { System.out.println(n); } } } |
2、使用Scanner从键盘读取一行输入字符串,去掉中间的重复字符,打印出最终的结果。
比如:输入aabbcccccdddd,则输出abcd
public class HashSetDemo03 { // 使用Scanner从键盘读取一行输入字符串,去掉中间的重复字符,打印出最终的结果。 // 比如:输入aabbaaacccccdddd,则输出abcd public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("请输入一行字符串:"); String str = scanner.next(); char[] arr = str.toCharArray(); Set<Character> set = new HashSet<Character>(); for (char ch : arr) { set.add(ch); }
// 输入set集合 for (Character ch : set) { System.out.print(ch); } } } |
四、TreeSet类:
4.1 TreeSet概述:
使用元素的自然顺序(升序)对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。
public class TreeSetDemo01 { public static void main(String[] args) { // TreeSet存储类库提供的引用类型的元素。 Set<Integer> set = new TreeSet<Integer>(); set.add(1); set.add(4); set.add(1); set.add(2);
System.out.println(set.toString()); } } |
自定义引用类型当做集合的元素类型:
public class PersonComparator implements Comparator<Person> {
@Override public int compare(Person person1, Person person2) { String name1 = person1.getName(); String name2 = person2.getName(); int name1Len = name1.length(); // 张三、张三 int name2Len = name2.length();
if (name1Len > name2Len) { return 1; } else if (name1Len < name2Len) { return -1; } else { // name1Len == name2Len for (int i = 0; i < name1Len; i++) { if (name1.charAt(i) > name2.charAt(i)) { return 1; } else if (name1.charAt(i) < name2.charAt(i)) { return -1; } else { // name1.charAt(i) == name2.charAt(i)
} } } return 0; } }
|
public class TreeSetDemo02 { public static void main(String[] args) { // Comparator接口,必须由类来实现 // TreeSet(Comparator<? super E> comparator) // 构造一个新的空 TreeSet,它根据指定比较器进行排序。 Set<Person> persons = new TreeSet<Person>(new PersonComparator()); persons.add(new Person("张三")); persons.add(new Person("李四")); persons.add(new Person("旺旺")); persons.add(new Person("张三"));
System.out.println(persons); } } |
4.2 TreeSet的原理:
1、特点:
用来排序的,可以指定一个顺序,对象存入之后会按照指定的顺序排列。
2、使用方式:
A、自然排序:
TreeSet类的add方法中会把存入的对象提升为Comparable类型。
调用对象的compareTo方法和集合中的对象比较。
根据compareTo方法返回值进行存储。
B、比较器排序(Comparator)
创建TreeSet时制定一个Comparator的一个实现类
add方法会调用比较器的compare方法进行比较,返回一个值。
根据compare方法的返回值进行存储。
4.2 面试题:
需求:控制台输入5个学生信息(姓名、语文成绩、数学成绩、英语成绩),按照总分从高到低输出到控制台。
public class StudentComparator implements Comparator<Student> { // 按照总分降序排列 @Override public int compare(Student stu1, Student stu2) { double total1 = stu1.getChinese() + stu1.getEnglish() + stu1.getMath(); double total2 = stu2.getChinese() + stu2.getEnglish() + stu2.getMath(); if (total1 > total2) { return -10; } else if (total1 < total2) { return 1; } return 0; } } |
public class Student { private String name; private double chinese; private double math; private double english;
public Student() { super(); // TODO Auto-generated constructor stub }
public Student(String name, double chinese, double math, double english) { super(); this.name = name; this.chinese = chinese; this.math = math; this.english = english; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public double getChinese() { return chinese; }
public void setChinese(double chinese) { this.chinese = chinese; }
public double getMath() { return math; }
public void setMath(double math) { this.math = math; }
public double getEnglish() { return english; }
public void setEnglish(double english) { this.english = english; }
@Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Student [name="); builder.append(name); builder.append(", chinese="); builder.append(chinese); builder.append(", math="); builder.append(math); builder.append(", english="); builder.append(english); double total = this.chinese + this.english + this.math; builder.append(", total=").append(total); builder.append("]"); return builder.toString(); } }
|
public class TreeSetDemo03 { // 需求:控制台输入5个学生信息(姓名、语文成绩、数学成绩、英语成绩),按照总分从高到低输出到控制台。 public static void main(String[] args) { Set<Student> students = new TreeSet<Student>(new StudentComparator()); // 假设是从控制台输入 students.add(new Student("张", 87.9, 90.0, 76.8)); students.add(new Student("王", 83.7, 92.0, 73.0)); students.add(new Student("李", 82.9, 94.0, 72.5)); students.add(new Student("郑", 84.9, 90.5, 76.0)); students.add(new Student("赵", 87.5, 86.0, 90.8)); for (Student student : students) { System.out.println(student); } } } |