1.LinkedList
List的实现类,有序可重复。
底层结构:双向链表,链表中数据以节点为单位;
链表结构的特点:增删效率高;根据索引查询遍历修改效率低;
应用场景:在大量做增删,少量做查询的位置适合使用LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
//新建学生对象
Student s1 = new Student("aaa",15);
Student s2 = new Student("bbb",20);
Student s3 = new Student("ccc",20);
//创建LinkedList对象
LinkedList<Student> ll = new LinkedList<Student>();
//增加Student对象元素
ll.add(s1);
ll.add(s2);
ll.add(s3);
System.out.println(ll);
//在首位增加元素
ll.addFirst(new Student("rrr",15));
//在末尾增加元素
ll.addLast(new Student("fff",18));
System.out.println(ll);
//获取第一个元素
System.out.println(ll.element());
//获取第一个元素
System.out.println(ll.getFirst());
//根据索引获取元素
System.out.println(ll.get(0));
System.out.println(ll.get(1));
System.out.println(ll.get(2));
}
}
class Student{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
}
当方法|构造器参数为接口类型的时候,实参可以考虑是否通过一个Lambda表达式进行传递,lambda可以让行为作为参数|数据传递,配合函数式接口可以让定义与实现变得更灵活。
public class ArrayListDemo {
public static void main(String[] args) {
User u1 = new User(100,3000);
User u2 = new User(101,5000);
User u3 = new User(102,2000);
User u4 = new User(103,4000);
//lambda表达式作为参数传递给TreeSet的构造器
TreeSet<User> aru = new TreeSet<User>((User o1, User o2)->{return o1.getUno()-o2.getUno();});
aru.add(u1);
aru.add(u2);
aru.add(u3);
aru.add(u4);
System.out.println(aru);
System.out.println("=====================");
User[] user = new User[4];
user[0] = u1;
user[1] = u2;
user[2] = u3;
user[3] = u4;
//使用Array.sort给数组排序,排序规则按照lambuda表达式
Arrays.sort(user,(User o1, User o2)->{return o1.getUno()-o2.getUno();});
System.out.println(Arrays.toString(user));
}
}
class User{
private int uno;
private int num;
public User(){
}
public User(int uno,int num){
this.num = num;
this.uno = uno;
}
public int getUno() {
return uno;
}
public void setUno(int uno) {
this.uno = uno;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
@Override
public String toString() {
return "User{" +
"uno=" + uno +
", num=" + num +
'}';
}
}
2.HashSet
Set接口的实现类,无序去重;
底层实现原理:哈希表(数组+链表+红黑树)
特点:查询即增删效率高
HashSet与TreeSet之间,如果想要数据存储的时候默认升序或降序推荐使用TreeSet;否则建议选择HashSet。
初始容量:16
扩容因子:0.75,当增加元素的索引达到初始容量的0.75时就会扩容。
哈希表:
- 数组+链表+红黑树——>jdk8;
- 数组+链表jdk7之前;
1、把数据计算hashcode()方法的返回值
int code = 数据.hashcode();
2、通过hash算法对code计算运算,结果就决定数据存在数组中桶的位置——>索引位置
in hash = code%数组长度;
hash是通过hash算法得到的结果,作为数组中桶的索引,数据存放的桶的位置
3、确定了数组中对应的索引位置(桶的位置),存储数据之前先判断,桶中是否已经存在当前这个数据值了,如果相等就不存储去重,如果没有相等,第一个数据就可以直接存储进去,以链表的形式连接到最后,或者作为链表头的存在;
确定两个数据值相等的方式:equals方法的返回值是true或者false,true就是相等,去重;false就是不相等,继续判断,当前数据与链表中所有的数据都不相等就能够存储。
public class HashSetPractice {
public static void main(String[] args) {
//创建HashSet对象
HashSet<String> hash = new HashSet<String>();
//增加元素
hash.add("abc");
hash.add("bcd");
hash.add("cde");
hash.add("def");
System.out.println(hash);
//使用迭代器进行遍历
Iterator<String> it = hash.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//使用增强for循环遍历
for(String s:hash){
System.out.println(s);
}
System.out.println("============");
HashSet<Person> hash2 = new HashSet<Person>();
Person p1 = new Person("小白",15);
Person p2 = new Person("小新",18);
Person p3 = new Person("小葵",14);
Person p4 = new Person("花轮",10);
Person p5 = new Person("花轮",10);
Person p6 = new Person("花轮",10);
//类中的hash.code进行重写后,去重
hash2.add(p1);
hash2.add(p2);
hash2.add(p3);
hash2.add(p4);
hash2.add(p5);
hash2.add(p6);
System.out.println(hash2);
}
}
class Person {
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.age = age;
this.name = name;
}
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
hashcode与equals:
- equals相等hashcode值肯定相等;
- hashcode相等,equals不一定相等;
3.Map
存储键值对的数据;
k-v一个映射关系——key——>value;
key与value可以为任意类型的一个数据;
一个key只能对应一个value;
key是唯一的,无序的 ———>Set集合
value是可重复的 ————>Collection无序可重复
遍历方式:
- keySet
- entrySet
- values
3.1HashMap
底层结构:哈希表(数组+链表+红黑树)
特点:
- 存储键值对类型数据;
- 无序,根据key做去重
- 查询,修改,删除,增加效率较高
Hash表存储原理:
哈希表:Node节点数组,存储节点数据,节点:key,value,hash,next;
1.put(key,value)存储键值对类型的数据,根据key计算hash值,确定桶的位置
2.找到Node数组中的指定索引位置,判断当前位置中是否存在数据,没有存在直接构建一个Node节点对象 new Node(key,value,hash,null),放入数组如果已经存在值,就比较每一个Node的key与我当前要存储的key是否相等,相等value覆盖,不相等继续判断,最后数据放入链表的最后
默认初始容量:1<<4即16,数组的长度;
加载因子:0.75
最大容量:1<<30
扩容:新数组的容量为原容量的2倍,当添加的数据个数>=原数组长度*0.75的时候就扩容,扩容的是数组的大小。
HashMap存储key如果为自定义引用类型数据时,key类型中要求重写hashcode与equals方法。
1、根据key的值计算hash值
调用hashcode——>hash算法——>得到位桶的索引
2、存放数据
找到对应的节点数组中位桶的位置,先比较缺点是否存在相同的key,然后再决定是否存储这个Node;
拿要存储的新节点的key与原链表中的数据比较,如果原数组中指定位置没有节点数据,新节点放入数组的对应位置,如果存在数据,依次拿key与每一链表中的节点的key比较equals是否相等,相等value覆盖,不相等则加入链表的最后。
值得注意的是:当链表中的数据个数超过8个,并且总容量超过64,则会把链表转为红黑树。
public class HashMapDemo {
public static void main(String[] args) {
HashMap<Integer,String> hmp = new HashMap<Integer, String>();
hmp.put(100,"a");
hmp.put(103,"b");
hmp.put(101,"c");
hmp.put(102,"d");
System.out.println(hmp);
System.out.println("============");
//keyset遍历
Set<Integer> set = hmp.keySet();
Iterator<Integer> it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
System.out.println("============");
//entryset遍历
Set<Map.Entry<Integer, String>> set2 = hmp.entrySet();
for(Map.Entry<Integer, String> entrys :set2){
System.out.println(entrys.getKey()+"-----》"+entrys.getValue());
}
System.out.println("============");
//values遍历
Collection<String> cl = hmp.values();
for(String s:cl){
System.out.println(s);
}
//替换 返回值boolean 替换成功返回true,失败返回false
System.out.println(hmp.replace(101,"c","haha"));
System.out.println(hmp);
//返回值:被替换的value值,
System.out.println(hmp.replace(102,"e"));
System.out.println(hmp);
System.out.println("=============");
Actor a1 = new Actor("aaa",15);
Actor a2 = new Actor("abc",16);
Actor a3 = new Actor("bbb",17);
Actor a4 = new Actor("ccc",15);
HashMap<Actor,Integer> hmp2 = new HashMap<Actor,Integer>();
System.out.println("===========");
//添加元素
System.out.println(hmp2.put(a1,15));
System.out.println(hmp2.put(a2,16));
System.out.println(hmp2.put(a3,17));
System.out.println(hmp2.put(a4,15));
//获取每一个元素的hashcode
System.out.println(hmp2.get(a1).hashCode());
System.out.println(hmp2.get(a2).hashCode());
System.out.println(hmp2.get(a3).hashCode());
System.out.println(hmp2.get(a4).hashCode());
//输出打印集合
System.out.println(hmp2);
}
}
class Actor{
private String name;
private int age;
public Actor(){}
public Actor(String name,int age){
this.age = age;
this.name = name;
}
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 "Actor{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Actor actor = (Actor) o;
return age == actor.age &&
Objects.equals(name, actor.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}