1、List集合
1.1 List
1、List集合的常用方法
boolean add(E e);
E remove(index i);
2、ArrayList集合是List集合的一个可变大小的数组实现
3、底层是一个数组,数组查询快,因为地址连续,且有索引;增删慢,因为地址不连续,每次增删要创建一个新数组
public class Demo01List {
public static void main(String[] args) {
List<String> list = new ArrayList<>();//多态
//add方法
list.add(“a”);
list.add(“b”);
list.add(“c”);
list.add(“d”);
list.add(“e”);
System.out.println(list);
//remove方法 删除a时,若集合中存在两个a,只会删除前一个
String removeE = list.remove(3);
System.out.println(“被移除的元素是:” + removeE + “当前集合:” + list);
//set方法
String replaceE = list.set(3, “E”);
System.out.println(“被替换的元素是:” + replaceE + “当前集合:” + list);
//get方法
//遍历集合 法一
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//法二
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
//法三
for (String s : list) {
System.out.println(s);
}
}
}
1.2 LinkedList
1、LinkedList是List的一个链表实现
2、底层是一个双向链表,查询慢,因为地址不连续,要从头开始查询,但找头节点和尾节点很快;增删快,因为增删操作不影响整个链表
3、不能使用多态创建对象,否则用不了子类方法
public class Demo02LinkedList {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
//添加元素的方法
//add方法
list.add(“a”);
list.add(“b”);
list.add(“c”);
System.out.println(list);
//addFirst方法
list.addFirst(“first”);
System.out.println(list);
//push方法,等价于addFirst方法
list.push(“second”);
System.out.println(list);
//addLast方法,等价于add方法
list.addLast(“last”);
System.out.println(list);
//获取元素的方法
//getFirst方法
//list.clear();//清空集合后获取元素会抛出异常NoSuchElementException,为了避免异常可以设置一个判断
if (!list.isEmpty()) {
String first = list.getFirst();
System.out.println(first);
//getLast方法
String last = list.getLast();
System.out.println(last);
}
//删除元素的方法
//removeFirst方法
String first = list.removeFirst();
System.out.println(first);
//removeLast方法
String last = list.removeLast();
System.out.println(last);
//pop方法,等价于removeFirst方法
String pop = list.pop();
System.out.println(pop);
System.out.println(list);
}
}
2、Set集合
2.1 Set
1、特点:(1)不能存储重复元素
(2)不带索引,没有带索引的方法,不能使用普通for循环遍历
2、Set接口的方法和Collection接口的方法一样
3、不能存储重复元素的原理:
在调用add方法时,会调用hashCode方法和equals方法,判断元素是否重复
前提:存储的元素必须重写hashCode方法和equals方法,保证元素不重复
哈希值一样是哈希冲突
public class Demo01Set {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
String s1 = “abc”;
String s2 = “abc”;
set.add(s1);//add方法会调用hashCode方法计算"abc"的哈希值96354,在数组中找有没有存储该哈希值的元素,没有就把96354存进数组,并将s1链接到该元素下面,即存进集合
set.add(s2);//add方法会调用hashCode方法计算"abc"的哈希值96354,在数组中找有没有存储该哈希值的元素。有就调用equals方法和哈希值相同的元素进行比较,s2.equals(s1),返回true,则认定两元素相同,就不存储s2到集合中
set.add(“重地”);//add方法会调用hashCode方法计算"重地"的哈希值1179395,在数组中找有没有存储该哈希值的元素,没有就把1179395存进数组,并将"重地"链接到该元素下面,即存进集合
set.add(“通话”);//add方法会调用hashCode方法计算"abc"的哈希值1179395,在数组中找有没有存储该哈希值的元素。有就调用equals方法和哈希值相同的元素进行比较,“通话”.equals(“重地”),返回false,则认定两元素不相同,就将"通话"链接到"重地"下面,即存进集合
set.add(“abc”);
System.out.println(set);//[重地, 通话, abc]
}
}
2.2 HashSet
1、底层是哈希表结构,查询速度非常快
2、无序,存储和取出元素的顺序可能不一致
3、实现不同步,支持多线程,速度比较快
4、jdk1.8之前,哈希表=数组+链表 数组初始容量为16 数组里存储元素的哈希值,相同哈希值的元素链接在该哈希值所在数组位置的下面
jdk1.8之后,哈希表=数组+链表/红黑树(链表长度超过8位,就会转换为红黑树)
new的是HashMap中的K,键唯一,所以不能存储相同元素
public class Demo02HashSet {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();//多态
//add方法
set.add(2);
set.add(1);
set.add(3);
set.add(1);
//使用迭代器遍历
Iterator<Integer> it = set.iterator();
while (it.hasNext()) {
Integer i = it.next();
System.out.println(i);//123 无序,且不能存储相同元素
}
//使用增强for遍历
for (Integer integer : set) {
System.out.println(integer);//123
}
}
}
2.3 哈希值
1、哈希值:一个十进制整数,由系统随机给出,就是对象的地址值,但是是一个逻辑地址,是模拟出来的地址,不是数据实际存储的物理地址
2、Object类中有一个获取对象哈希值的方法,public native int hashCode();没有方法体,调用本地操作系统的方法
Person类
public class Person {
}
测试类
public class Demo03HashValue {
public static void main(String[] args) {
Person p1 = new Person();//Person类默认继承Object,所以可以调用Object的hashCode方法
int h1 = p1.hashCode();
System.out.println(h1);//2083562754 随机十进制整数
System.out.println(p1);//cn.ircast.demo02Set.Person@7c30a502 toString方法最后的值就是哈希值 7c30a502与2083562754相等
//可以重写hashCode方法,返回一个常数,但这不代表两个对象的地址相等,因为这不是实际的物理地址
//String类就重写了hashCode方法,当两个字符串的内容一样,返回的哈希值也一样
String s1 = “asd”;
String s2 = “asd”;
System.out.println(s1.hashCode());//96882
System.out.println(s2.hashCode());//96882
System.out.println(“重地”.hashCode());//1179395
System.out.println(“通话”.hashCode());//1179395
}
}
2.4 HashSet存储我们自定义的类型的元素
让HashSet存储我们自定义的类型元素,必须重写hashCode和equals方法,不使用Set集合,可以不重写
同名同年龄的学生,视为同一元素
定义学生类
public class Student {
private String name;
private int age;
@Override
public String toString() {
return “Student{” +
“name=’” + name + ‘’’ +
“, 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;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
定义测试类使用HashSet存储学生类的元素
public class Demo04HashSetCustomize {
public static void main(String[] args) {
HashSet<Student> set = new HashSet<>();
Student s1 = new Student(“张三”, 18);
Student s2 = new Student(“张三”, 18);
Student s3 = new Student(“李四”, 19);
set.add(s1);
set.add(s2);
set.add(s3);
System.out.println(set);
//不重写hashCode方法的话,两个对象的哈希值不同
//不重写equals方法的话,比较两个对象的地址值,也就是哈希值
// [Student{name=‘张三’, age=18}, Student{name=‘张三’, age=18},Student{name=‘李四’, age=19}]
//重写后[Student{name=‘张三’, age=18}, Student{name=‘李四’, age=19}]
}
}
2.5 LinkedHashSet
特点:底层是一个哈希表(数组+链表/红黑树)+链表,多了一条链表,用来记录元素的存储顺序,保证元素有序
public class Demo05LinkedHashSet {
public static void main(String[] args) {
HashSet<String> set1 = new HashSet<>();
set1.add(“hello”);
set1.add(“world”);
set1.add(“java”);
System.out.println(set1);//[world, java, hello] 无序
LinkedHashSet<String> set2 = new LinkedHashSet();
set2.add(“hello”);
set2.add(“world”);
set2.add(“java”);
System.out.println(set2);//[hello, world, java] 有序
}
}
2.6 可变参数
1、当方法的参数列表的数据类型确定,而参数个数不确定就可以使用可变参数
修饰符 返回值类型 方法名(数据类型…变量名){}
2、底层是一个数组,根据传递的参数个数的不同,创建不同大小的数组,来存储这些参数,可以是0个参数
3、注意:
(1)一个方法的参数列表只能有一个可变参数
(2)可变参数要写在参数列表的末尾
public class Demo06VariablePara {
public static void main(String[] args) {
System.out.println(add());//0 创建一个长度为0的数组
System.out.println(add(10));//10 创建一个长度为1的数组
System.out.println(add(10, 20));//30
System.out.println(add(10, 20, 30));//60
}
//计算0-n个整数的和
public static int add(int…arr) {//arr存储的是数组的地址
int sum = 0;
for (int i = 0; i < arr.length; i++) {
sum += arr[i];
}
return sum;
}
//可变参数的终极写法
public static void method(Object…o) {//可以接收任意类型的参数
}
}
3、集合工具类Collections
常用方法
static boolean addAll(Collection c, T…elements)//往集合中添加一些元素
static void shuffle(List<?> list)//打乱集合顺序
static void sort(List list)//给集合按默认规则排序,升序
static void sort(List list, Comparator<? super T>)//给集合按指定规则排序
要对自定义类型进行排序,需要实现Comparable接口,重写compareTo方法,定义排序规则
Comparable 自己和别人比较,自己需要实现Comparable接口,重写compareTo方法,定义比较规则
Comparator 找一个第三方比较二者
Person类
不使用Set集合,可以不重写hashCode方法和equals方法
要调用Collections类的sort方法,必须实现Comparable接口,重写compareTo方法
public class Person implements Comparable {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
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
public int compareTo(Person o) {
//return 0;//认为元素都是相同的
//自定义比较规则
return this.getAge() - o.getAge();//升序
}
}
测试类
public class Demo01CollectionsMethod {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
//addAll方法
Collections.addAll(list, “hello”, “world”, “like”, “java”);
System.out.println(list);//[hello, world, like, java]
//shuffle方法
Collections.shuffle(list);
System.out.println(list);//[world, hello, java, like]
//sort方法,默认排序,只能对List排序,不能对Set排序
//Collections.sort(list);
//System.out.println(list);//[hello, java, like, world]
//对自定义的类型进行排序
ArrayList list1 = new ArrayList<>();
Collections.addAll(list1, new Person(“张三”, 18), new Person(“李四”, 17), new Person(“王五”, 20), new Person(“刘六”, 18));
System.out.println(list1);//[Person{name=‘张三’, age=18}, Person{name=‘李四’, age=17}, Person{name=‘王五’, age=20}]
//Collections.sort(list1);
//System.out.println(list1);//[Person{name=‘李四’, age=17}, Person{name=‘张三’, age=18}, Person{name=‘王五’, age=20}]
Collections.sort(list, new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
System.out.println(list);
// Collections.sort(list1, new Comparator() {
// @Override
// public int compare(Person o1, Person o2) {
// return o1.getAge() - o2.getAge();//升序
// }
// });
Collections.sort(list1, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
int result = o1.getAge() - o2.getAge();
if(result == 0) {//年龄相等,比较姓名字符串
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
System.out.println(list1);
}
}