集合:是存储数据的容器。
Collection接口:集合体系的根接口。
Collection的常用方法:
/**
* 所有单列集合都是Collection接口的实现类,所有以下的方法所有的集合都能够调用。
*/
public class Demo2 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
//1.add(E e): 添加元素
list.add("孙悟空");
list.add("孙悟空");
list.add("猪八戒");
list.add("猪八戒");
list.add("沙和尚");
list.add("白龙马");
list.add("唐僧");
//2.remove(E e) 删除集合中的第一个相同的元素
list.remove("孙悟空");
System.out.println(list);
//3.removeif(E e)
list.removeIf(s -> s.equals("猪八戒"));
System.out.println(list);
//4.contains(Object obj) 判断集合中是否包含某一个元素
System.out.println(list.contains("白龙马"));
//5.isEmpty() 判断集合是否为空(长度为0表示为空)
list.isEmpty();
//6.size() 获取集合的元素个数
System.out.println(list.size());
//7.toArray() 把集合容器转换为数组容器
list.toArray();
System.out.println(list);
//8.clear()清空集合中的元素
list.clear();
System.out.println(list);
}
}
Collection集合的遍历方式:
方式一:迭代器:
/**
* 迭代器遍历
*/
public class Demo3 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("孙悟空");
list.add("猪八戒");
list.add("沙和尚");
list.add("小白龙");
list.add("小白龙");
//迭代器遍历集合(有无索引无影响,通用遍历方式)
//1.返回集合中的迭代器对象,该迭代器对象默认指向当前集合的0索引。
Iterator<String> it = list.iterator();
//2.询问当前位置是否有元素存在。
while (it.hasNext()){
//3.获取当前位置的元素,同时将迭代器对象移到下一个位置,注意防止取出越界。
System.out.println(it.next());
}
}
方式二:增强for循环:
本质上就是迭代器。
/** * 增强for遍历 * for(元素数据类型 变量名:数组/集合){ * ... * } */ public class Demo4 { public static void main(String[] args) { Collection<String> list = new ArrayList<>(); list.add("孙悟空"); list.add("猪八戒"); list.add("沙和尚"); list.add("小白龙"); list.add("小白龙"); int[] arr={10,23,56}; //增强for遍历:既可以遍历集合,也可以遍历数组。 for (String s : list) { System.out.println(s); } for (int i : arr) { System.out.println(i); } } }
注意:迭代器遍历集合有一个问题(并发修改异常)。
使用迭代器遍历集合时,集合自己不能对添加,删除元素,否则会出现
方式三:forEach方法遍历(结合lambda遍历集合):
/**
* forEach方法遍历集合
*/
public class Demo6 {
public static void main(String[] args) {
Collection<String> list = new ArrayList<>();
list.add("孙悟空");
list.add("猪八戒");
list.add("沙和尚");
list.add("小白龙");
list.add("小白龙");
/**
* forEach方法底层会自己遍历集合,获取到每一个元素,然后把元素交给Consumer接口的实现类对象的accept方法
* 对于accept方法拿着元素去干什么,由我们调用者决定。
*/
// list.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// System.out.println(s+"123");
// }
// });
//调用forEach方法遍历集合
list.forEach(s -> System.out.println(s+"123"));
}
}
list系列集合(有索引):
public class Demo1 { public static void main(String[] args) { List<String> list = new ArrayList<>(); list.add("孙悟空"); list.add("猪八戒"); list.add("沙和尚"); list.add("小白龙"); list.add("小白龙"); //1.add(index,element):在指定位置插入元素。 list.add(0,"白骨精"); System.out.println(list); //2.remove(index):删除指定索引处的元素。 String removed=list.remove(1); System.out.println(removed); //3.set(index,element):修改指定索引的元素。 list.set(0,"如来"); System.out.println(list); //4.get(index)返回指定索引的元素 System.out.println(list.get(0)); } }
注意:ArrayList和LinkedList都可以使用上面的方法。
ArrayLIst底层是由数组实现的。(数组是一种查询快,增删慢的模型)
LinkedList集合底层是一个双向链表,一个元素对应一个节点。(链表是一种查询慢,增删快的模型)
/**
* LinkedList集合的特有方法
*/
public class Demo2 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//1.添加头节点元素
list.addFirst("hello");
//2.添加尾节点元素
list.addLast("Workld");
//3.获取头节点元素
System.out.println(list.getFirst());
//4.获取尾节点元素
System.out.println(list.getLast());
//5.删除头节点元素
System.out.println(list.removeFirst());
//6.删除尾节点元素
System.out.println(list.removeLast());
}
}
LinkedList集合多了一些针对头和尾操作的方法。
Set系列的集合:
哈希值:哈希值是一个与地址值相关联的整数,任何一个对象都有哈希值。
在Object类中有一个hashCode()方法,任何一个对象都可以调用hashCode()方法获取哈希值。
注意:一个类不复写hashCode方法,这个类的每个对象的哈希值都不一样。
一个类复写了hashCode方法,这个类的对象只要属性值一样,那么哈希值就一样。
HashSet集合底层原理:
HashSet底层是:数组+链表组成(哈希表结构)
JDK8版本后是:数组+链表+红黑树组成的(哈希表结构)
步骤:
- 创建一个默认长度16的数组,数组名table
- 根据元素的哈希值跟数组的长度求余数计算出应该存入的位置。(哈希算法)
- 判断存入位置是否为null,如果为null则直接存入。如果不为null,说明这个位置有元素,需要使用equals进一步判断。
- 如果equals判断不同,则以链表的形式插入元素。如果相同,则认为是同一个元素,不存入。
- 当一个位置的元素个数超过了8个,会自动转成红黑树存储。进一步提高操作数据的性能。
注意:如果希望Set集合认为2个内容相同的对象是重复的怎么办?
重写对象的hashCode和equals方法。
LinkedHashSet底层原理:
有序,不重复,无索引。
原理:底层数据结构依然是哈希表,只是每个元素又额外地多了一个双链表的机制记录存储的顺序。
TreeSet集合底层原理:
TreeSet集合特点:
1.可排序,不重复,无索引
2.底层基于红黑树实现排序,增删改查性能较好
红黑树是二叉排序树的一种。
二叉排序树:由一个一个的节点组成,每一个节点有两个子节点,左边的节点都比自己小,右边的节点都比自己大。
规则:
小的存左边
大的存右边
一样的不存
TreeSet指定排序规则(默认排序)
在源代码中,比如String,Integer,Double等,本身就是实现了Comparable接口,编写了默认排序规则。所以这些类型的元素,只要往TreeSet集合中存储就会自动排序。
但是,存储自定义类型的元素(比如Student对象),就必须手动让Student类实现Comparable接口,复写compareto方法。
排序原理:TreeSet调用add方法时,底层会自动调用Comparable实现类对象compareTo方法,将添加的元素和集合中已有的元素进行比较,根据compareTo方法的返回值是正数,负数,或者0,来决定谁大谁小,从而将添加的元素放在正确的位置。
TreeSet指定排序规则(比较器排序)
在创建TreeSet集合时,可以传递一个Compatator接口的实现类对象(称为比较器),此时TreeSet集合优先使用比较器排序,如果没有比较器,再使用默认排序。