当前位置:首页 > Java技术 > Java集合

Java集合

2022年08月05日 09:24:29Java技术6

集合概述


  • 概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。

  • 和数组的区别:

  1. 数组长度固定,集合长度不固定。

  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型。

  • 位置: java.util.*;

 

Collection体系集合


Java集合 _ JavaClub全栈架构师技术笔记

Collection父接口


  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。

  • 方法:

    • boolean add(Object obj) //添加一个对象。
    • boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
    • void clear() //清空此集合中的所有对象。
    • boolean contains(Object o) //检查此集合中是否包含o对象。
    • boolean equals(Object o) //比较此集合是否与指定对象相等。
    • boolean isEmpty() //判断此集合是否为空。
    • boolean remove(Object o) //在此集合中移除o对象。
    • int size() //返回此集合中的元素个数。
    • Object[] toArray() //姜此集合转换成数组。
 1 /**
 2  * Collection接口的使用(一)
 3  * 1.添加元素
 4  * 2.删除元素
 5  * 3.遍历元素
 6  * 4.判断
 7  */
 8 public class Demo01 {
 9     public static void main(String[] args) {
10         //创建集合
11         Collection collection = new ArrayList();
12 
13 //      * 1.添加元素
14         collection.add("西瓜");
15         collection.add("苹果");
16         collection.add("榴莲");
17         System.out.println(collection.size());
18         System.out.println(collection.toString());
19 
20 //      * 2.删除元素
21         collection.remove("西瓜");
22         System.out.println("删除之后:"+collection.size());
23 
24 //      * 3.遍历元素
25         //3.1 使用增强for
26         for (Object object:collection) {
27             String s = (String) object;
28             System.out.println(s);
29         }
30         //3.2 使用迭代器(迭代器专门用来遍历集合的一种方式)
31         //hasnext();判断是否有下一个元素
32         //next();获取下一个元素
33         //remove();删除当前元素
34         Iterator it = collection.iterator();
35         while (it.hasNext()){
36             String s = (String) it.next();
37             System.out.println(s);
38             //删除操作
39             //collection.remove(s);引发错误:并发修改异常
40             //iterator.remove();应使用迭代器的方法
41 
42 //      * 4.判断
43             System.out.println(collection.contains("苹果"));//true 是否存在苹果
44             System.out.println(collection.isEmpty());//false 是否有数据,没有数据为true,有数据为false
45         }
46     }
47 }
 1 /**
 2  * Collection接口的使用(二)
 3  * 1.添加元素
 4  * 2.删除元素
 5  * 3.遍历元素
 6  * 4.判断
 7  */
 8 public class Demo02 {
 9     public static void main(String[] args) {
10 
11         //创建Collection对象
12         Collection collection = new ArrayList();
13 
14         //创建Student对象
15         Student s1 = new Student("张三",18);
16         Student s2 = new Student("李四",19);
17         Student s3 = new Student("王五",20);
18 
19         //1.添加数据
20         collection.add(s1);
21         collection.add(s2);
22         collection.add(s3);
23         //collection.add(s3);可重复添加相同对象
24         System.out.println("元素个数为:"+collection.size());
25         System.out.println(collection.toString());
26 
27         //2.删除数据
28         collection.remove(s1);
29         System.out.println("元素个数为:"+collection.size());
30         System.out.println(collection.toString());
31 
32         //3.遍历数据
33         //3.1 增强for循环
34         for (Object object:collection) {
35             Student s = (Student) object;
36             System.out.println(object);
37         }
38 
39         //3.2 迭代数据
40         //迭代过程中不能使用Collection的删除方法,即Collection.remove()
41         Iterator it = collection.iterator();
42         while (it.hasNext()){
43             Student s = (Student) it.next();
44             System.out.println(s);
45             //删除
46             //it.remove();
47         }
48 
49         //4判断
50         System.out.println(collection.contains("张三"));//false
51         System.out.println(collection.contains(s2));//true
52         System.out.println(collection.isEmpty());//false
53     }
54 }
 1 /**
 2  * 学生类
 3  */
 4 public class Student {
 5     private String name;
 6     private int age;
 7 
 8     //有参构造
 9     public Student(String name, int age) {
10         this.name = name;
11         this.age = age;
12     }
13 
14     //无参构造
15     public Student() {
16     }
17 
18     //重写toString
19     @Override
20     public String toString() {
21         return "Student{" +
22                 "name='" + name + '\'' +
23                 ", age=" + age +
24                 '}';
25     }
26 
27     //get/set静态属性
28     public String getName() {
29         return name;
30     }
31 
32     public void setName(String name) {
33         this.name = name;
34     }
35 
36     public int getAge() {
37         return age;
38     }
39 
40     public void setAge(int age) {
41         this.age = age;
42     }
43 }

 

Collection子接口


List集合

  • 特点:有序、有下标、元素可以重复。

  • 方法:

    • void add(int index,Object o) //在index位置插入对象o。
    • boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
    • Object get(int index) //返回集合中指定位置的元素。
    • List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。
 1 /**
 2  * List子接口的使用(一)
 3  * 特点:1.有序有下标 2.可以重复
 4  *
 5  * 1.添加元素
 6  * 2.删除元素
 7  * 3.遍历元素
 8  * 4.判断
 9  * 5.获取位置
10  */
11 public class Demo03 {
12     public static void main(String[] args) {
13 
14         //1.创建list集合对象
15         List list = new ArrayList<>();
16 
17         //2.添加元素
18         list.add("迪迦");
19         list.add("艾斯");
20         list.add(0,"赛文");//根据index下标插入数据
21         System.out.println("元素个数为:"+list.size());
22         System.out.println(list.toString());
23 
24         //3.删除数据
25         list.remove(1);
26         //list.remove("迪迦");意思同上,一个是根据角标,一个是根据内容
27         System.out.println("删除之后:"+list.size());
28         System.out.println(list.toString());
29 
30         //4.遍历元素
31         //4.1 因为有下标,所以可以使用for循环
32         for (int i = 0; i < list.size(); i++) {
33             System.out.println(list.get(i));
34         }
35         //4.2 使用增强for循环
36         for (Object object:list) {
37             String s = (String) object;
38             System.out.println(s);
39         }
40         //4.3 使用迭代器
41         Iterator it = list.iterator();
42         while (it.hasNext()){
43             String s = (String) it.next();
44             System.out.println(s);
45         }
46         //4.4 使用列表迭代器
47         ListIterator listIterator = list.listIterator();
48         //从前往后遍历
49         while (listIterator.hasNext()){
50             String s = (String) listIterator.next();
51             System.out.println(s);
52         }
53         //从后往前遍历(此时“遍历指针”已经指向末尾了)
54         while (listIterator.hasPrevious()){
55             String s = (String) listIterator.previous();
56             System.out.println(s);
57         }
58 
59         //5.判断
60         System.out.println(list.contains("赛文"));//true
61         System.out.println(list.isEmpty());//false
62 
63         //6.获取位置
64         System.out.println(list.indexOf("艾斯"));//1
65     }
66 }
 1 /**
 2  * List子接口的使用(二)
 3  * 1.添加元素
 4  * 2.删除元素
 5  * 3.遍历元素
 6  * 4.判断
 7  * 5.获取位置
 8  */
 9 public class Demo04 {
10     public static void main(String[] args) {
11         List list = new ArrayList<>();
12         //1.添加基本类型数字数据(自动装箱)
13         list.add(10);
14         list.add(20);
15         list.add(30);
16         list.add(40);
17         list.add(40);
18         System.out.println("元素个数为:"+list.size());
19         System.out.println(list.toString());
20 
21         //2.删除数据
22         list.remove(0);//根据下标删除
23         //list.remove(40);下标越界
24         list.remove(new Integer(40));//根据内容删除,将基本类型转成引用类型
25         System.out.println("删除后:"+list.size());
26         System.out.println(list.toString());
27 
28         //3.遍历元素
29         //3.1 使用for循环
30         for (int i = 0; i < list.size(); i++) {
31             System.out.println(list.get(i));
32         }
33         //3.2 使用增强for循环
34         for (Object object:list) {
35             int s = (int) object;
36             System.out.println(s);
37         }
38         //3.3 使用迭代器
39         Iterator it = list.iterator();
40         while (it.hasNext()){
41             int s = (int) it.next();
42             System.out.println(s);
43         }
44         //3.4 使用列表迭代器
45         ListIterator listIterator = list.listIterator();
46         //从前往后遍历
47         while (listIterator.hasNext()){
48             int s = (int) listIterator.next();
49             System.out.println(s);
50         }
51         //从后往前遍历(此时“遍历指针”已经指向末尾了)
52         while (listIterator.hasPrevious()){
53             int s = (int) listIterator.previous();
54             System.out.println(s);
55         }
56 
57         //4.判断
58         System.out.println(list.contains(20));//true
59         System.out.println(list.isEmpty());//false
60 
61         //5.获取位置
62         System.out.println(list.indexOf(30));//1
63 
64         //6.补充方法sublist,根据角标范围,返回集合元素,含头不含尾
65         List list1 = list.subList(0,2);
66         System.out.println(list1.toString());//20,30
67     }
68 }

List实现类

ArrayList【重点】
  • 数组结构实现,查询快、增删慢;
  • JDK1.2版本,运行效率快、线程不安全。
 1 /**
 2  * ArrayList的使用
 3  * 存储结构:数组;
 4  * 特点:查找遍历速度快,增删慢。
 5  * 1.添加元素
 6  * 2.删除元素
 7  * 3.遍历元素
 8  * 4.判断
 9  * 5.查找
10  */
11 public class Demo05 {
12     public static void main(String[] args) {
13         ArrayList arrayList = new ArrayList<>();
14         //1.添加元素
15         Student s1 = new Student("钢铁侠",3);
16         Student s2 = new Student("蜘蛛侠",4);
17         Student s3 = new Student("蝙蝠侠",5);
18         arrayList.add(s1);
19         arrayList.add(s2);
20         arrayList.add(s3);
21         arrayList.add(s3);
22         System.out.println("元素个数:"+arrayList.size());
23         System.out.println(arrayList.toString());
24 
25         //2.删除元素
26         arrayList.remove(s3);
27         System.out.println("删除后:"+arrayList.size());
28         System.out.println(arrayList.toString());
29         //arrayList.remove(new Student("唐", 21));
30         //注:这样可以删除吗(不可以)?显然这是两个不同的对象。
31         //假如两个对象属性相同便认为其是同一对象,那么如何修改代码?
32 
33         //3.遍历元素
34         //3.1使用迭代器
35         Iterator it = arrayList.iterator();
36         while (it.hasNext()){
37             Student s = (Student) it.next();
38             System.out.println(s);
39         }
40         //3.2使用列表迭代器
41         ListIterator listIterator = arrayList.listIterator();
42         //从前往后遍历
43         while (listIterator.hasNext()){
44             Student s = (Student) listIterator.next();
45             System.out.println(s);
46         }
47         //从后往前遍历
48         while (listIterator.hasPrevious()){
49             Student s = (Student) listIterator.previous();
50             System.out.println(s);
51         }
52 
53         //4.判断
54         System.out.println(arrayList.isEmpty());//false
55         //System.out.println(arrayList.contains(new Student("钢铁侠", 3)));
56         //注:与上文相同的问题。
57 
58         //5.查找
59         System.out.println(arrayList.indexOf(s1));
60     }
61 }

注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:

 1     //重写equls方法
 2     @Override
 3     public boolean equals(Object obj) {
 4         //1.是否为同一对象
 5         if (this == obj){
 6             return true;
 7         }
 8         //2.判断是否为空
 9         if (obj == null){
10             return false;
11         }
12         //3.判断是否是Student类型
13         if (this instanceof Student){
14             Student s = (Student) obj;
15             //4.比较属性
16             if (this.name.equals(s.getName())&&this.age==s.age){
17                 return true;
18             }
19         }
20         return false;
21     }
ArrayList源码分析
  • 默认容量大小:private static final int DEFAULT_CAPACITY = 10;

  • 存放元素的数组:transient Object[] elementData;

  • 实际元素个数:private int size;

  • 创建对象时调用的无参构造函数:

1 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2 //Arraylist无参构造方法
3 public ArrayList() {
4         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
5 }

这段源码说明当你没有向集合中添加任何元素时,集合容量为0。那么默认的10个容量怎么来的呢?这就得看看add方法的源码了:

1     public boolean add(E e) {
2         ensureCapacityInternal(size + 1);  // Increments modCount!!
3         elementData[size++] = e;
4         return true;
5     }

假设你new了一个数组,当前容量为0,size当然也为0。这时调用add方法进入到ensureCapacityInternal(size + 1);该方法源码如下:

1     private void ensureCapacityInternal(int minCapacity) {
2         if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
3             minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
4         }
5 
6         ensureExplicitCapacity(minCapacity);
7     }

由于一开始Arraylist的无参构造方法,就是将 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给了 elementData  ,所以if的条件是成立的。

Java集合 _ JavaClub全栈架构师技术笔记

if语句中 minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); 就是在 DEFAULT_CAPACITY 和 minCapacity 中返回一个最大的参数, DEFAULT_CAPACITY 是Arrarylist中一开始就定义的静态常量,大小为10。 minCapacity 传入的值为size+1也就是 1

Java集合 _ JavaClub全栈架构师技术笔记

DEFAULT_CAPACITY值大,因此返回DEFAULT_CAPACITY的值为10,这个值作为参数又传入ensureExplicitCapacity()方法中,进入该方法查看源码:

1     private void ensureExplicitCapacity(int minCapacity) {
2         modCount++;
3 
4         // overflow-conscious code
5         if (minCapacity - elementData.length > 0)
6             grow(minCapacity);
7     }

我们先不要管modCount这个变量,可以理解为这个变量是记录数组修改次数。

因为elementData数组长度为0,minCapacity为10,所以if条件成立,调用grow方法,重要的部分来了,这个代码就是数组扩容的代码,现在我们进入到grow方法的源码中:

 1     private void grow(int minCapacity) {
 2         // overflow-conscious code
 3         int oldCapacity = elementData.length;  //oldCapactity=0
 4         int newCapacity = oldCapacity + (oldCapacity >> 1);  //oldCapacity=0;(oldCapacity >> 1)0右移一位还是0;因此newCapacity=0
 5         if (newCapacity - minCapacity < 0)  //0-10=-10,if条件成立
 6             newCapacity = minCapacity;    //newCapacity=10
 7         if (newCapacity - MAX_ARRAY_SIZE > 0)  //MAX_ARRAY_SIZE这个值大家也可以进去看下,是好几个亿,这个条件基本上永远不会成立。可以忽略不看,了解即可
 8             newCapacity = hugeCapacity(minCapacity);
 9         // minCapacity is usually close to size, so this is a win:
10         elementData = Arrays.copyOf(elementData, newCapacity);  //copyOf(elementData,newCapacity)创建一个新的数组elementDate,数组长度为newCapacity即就是10
11     }

这个方法先声明了一个oldCapacity变量将数组长度赋给它,其值为0;又声明了一个newCapacity变量其值为oldCapacity+一个增量,可以发现这个增量是和原数组长度有关的量,当然在这里也为0。第一个if条件满足,newCapacity的值为10(这就是默认的容量,不理解的话再看看前面)。第二个if条件不成立,也可以不用注意,因为MAX_ARRAY_SIZE的定义如下:

Java集合 _ JavaClub全栈架构师技术笔记

这个值太大了以至于第二个if条件没有了解的必要。

最后一句话就是为elementData数组赋予了新的长度,Arrays.copyOf()方法返回的数组是新的数组对象,原数组对象不会改变,该拷贝不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值。

这时候再回到add的方法中,接着就向下执行elementData[size++] = e,将add传入的值e赋值给数组elementData,下标index为[size++]。到这里为止关于ArrayList就讲解得差不多了,当数组长度为10的时候你们可以试着过一下源码,查一下每次的增量是多少(答案是每次扩容为原来的1.5倍)。

Java集合 _ JavaClub全栈架构师技术笔记

 


 Vector
  • 数组结构实现,查询快、增删慢;

  • JDK1.0版本,运行效率慢、线程安全。

 1 /**
 2  * Vector的演示使用
 3  *
 4  *1.添加数据
 5  *2.删除数据
 6  *3.遍历
 7  *4.判断
 8  */
 9 public class Demo01 {
10     public static void main(String[] args) {
11         //1.创建Vector对象
12         Vector v = new Vector<>();
13 
14         //2.添加数据
15         v.add("孙悟空");
16         v.add("猪八戒");
17         v.add("沙和尚");
18         System.out.println("元素个数为:"+v.size());
19         System.out.println(v.toString());
20 
21         //3.删除数据
22         v.remove("沙和尚");
23         //v.remove(2);效果同上
24         System.out.println("删除后:"+v.size());
25         System.out.println(v.toString());
26 
27         //4.遍历
28         //使用枚举
29         Enumeration elements = v.elements();
30         while (elements.hasMoreElements()){
31             System.out.println(elements.nextElement());
32         }
33 
34         //5.判断
35         System.out.println(v.isEmpty());
36         System.out.println(v.contains("孙悟空"));
37     }
38 }

 


LinkedList
  • 链表结构实现,增删快,查询慢。
 1 /**
 2  * LinkedList的用法
 3  * 存储结构:双向链表
 4  * 1.创建对象
 5  * 2.添加元素
 6  * 3.删除元素
 7  * 4.遍历
 8  * 5.判断
 9  */
10 public class Demo01 {
11     public static void main(String[] args) {
12         //1.创建LinkedList对象
13         LinkedList link = new LinkedList<>();
14 
15         //2.添加元素
16         Student s1 = new Student("python",1);
17         Student s2 = new Student("Java",2);
18         Student s3 = new Student("go",3);
19         link.add(s1);
20         link.add(s2);
21         link.add(s3);
22         link.add(s3);
23         System.out.println("元素个数为:"+link.size());
24         System.out.println(link.toString());
25 
26         //3.删除元素
27         link.remove(s3);
28         link.remove(new Student("go",3));
29         System.out.println("删除后:"+link.size());
30         System.out.println(link.toString());
31 
32         //4.遍历
33         //4.1 使用for循环遍历
34         for (int i = 0; i < link.size(); i++) {
35             System.out.println(link.get(i));
36         }
37         //4.2 使用增强for
38         for (Object object:link) {
39             Student s = (Student) object;
40             System.out.println(s.toString());
41         }
42         //4.3 使用迭代器
43         Iterator it = link.iterator();
44         while (it.hasNext()){
45             System.out.println(it.next());
46         }
47         //4.4 使用列表迭代器
48         ListIterator listIterator = link.listIterator();
49         //从前往后遍历
50         while (listIterator.hasNext()){
51             System.out.println(listIterator.next());
52         }
53         //从后往前遍历
54         while (listIterator.hasPrevious()){
55             System.out.println(listIterator.previous());
56         }
57 
58         //5.判断
59         System.out.println(link.isEmpty());
60         System.out.println(link.contains(s1));
61         System.out.println(link.contains(new Student("Java",2)));
62     }
63 }

 

LinkedList源码分析

Java集合 _ JavaClub全栈架构师技术笔记

LinkedList首先有三个属性:

  • 链表大小:transient int size = 0;
  • (指向)第一个结点/头结点: transient Node<E> first;
  • (指向)最后一个结点/尾结点:transient Node<E> last;

关于Node类型我们再进入到类里看看:

 1     private static class Node<E> {
 2         E item;
 3         Node<E> next;
 4         Node<E> prev;
 5 
 6         Node(Node<E> prev, E element, Node<E> next) {
 7             this.item = element;
 8             this.next = next;
 9             this.prev = prev;
10         }
11     }

首先item存放的是实际数据;next指向下一个结点而prev指向上一个结点。

Node带参构造方法的三个参数分别是前一个结点、存储的数据、后一个结点,调用这个构造方法时将它们赋值给当前对象。

LinkedList是如何添加元素的呢?先看看add方法:

1     public boolean add(E e) {
2         linkLast(e);
3         return true;
4     }

进入到linkLast方法:

 1     void linkLast(E e) {
 2         final Node<E> l = last;
 3         final Node<E> newNode = new Node<>(l, e, null);
 4         last = newNode;
 5         if (l == null)
 6             first = newNode;
 7         else
 8             l.next = newNode;
 9         size++;
10         modCount++;
11     }

假设刚开始new了一个LinkedList对象,first和last属性都为空,调用add进入到linkLast方法。

首先创建一个Node变量 l 将last(此时为空)赋给它,然后new一个newNode变量存储数据,并且它的前驱指向l,后继指向null;再把last指向newNode。如下图所示:

如果满足if条件,说明这是添加的第一个结点,将first指向newNode:

至此,LinkedList对象的第一个数据添加完毕。假设需要再添加一个数据,我们可以再来走一遍,过程同上不再赘述,图示如下:


ArrayList和LinkedList区别
  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。


泛型概述

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
  • 常见形式有泛型类、泛型接口、泛型方法。
  • 语法:
    • <T,…> T称为类型占位符,表示一种引用类型。
  • 好处:
    • 提高代码的重用性。
    • 防止类型转换异常,提高代码的安全性。
泛型类
 1 /**
 2  * 泛型类
 3  * 语法:类名<T>
 4  * T是类型占位符,表示一种引用类型,编写多个使用逗号隔开
 5  *
 6  */
 7 public class MyGeneric<T> {
 8     //1.创建泛型变量
 9     //不能使用new来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法
10     T t;
11     //2.泛型作为方法的参数
12     public void show(T t){
13         System.out.println(t);
14     }
15 
16     //3.泛型作为方法的返回值
17     public T getT(){
18         return t;
19     }
20 }
 1 /**
 2  * 注意:
 3  * 1.泛型只能使用引用类型
 4  * 2.不同泛型类型的对象不能相互赋值
 5  */
 6 public class TestGeneric {
 7     public static void main(String[] args) {
 8         //使用泛型类创建对象
 9         MyGeneric<String> stringMyGeneric = new MyGeneric<>();
10         stringMyGeneric.t = "葫芦娃";
11         stringMyGeneric.show("火娃");
12 
13         MyGeneric<Integer> integerMyGeneric = new MyGeneric<>();
14         integerMyGeneric.t=20;
15         integerMyGeneric.show(30);
16         Integer integer = integerMyGeneric.getT();
17         System.out.println(integer);
18     }
19 }
泛型接口
 1 /**
 2  * 泛型接口
 3  * 语法:接口名<T>
 4  * 注意:不能创建泛型静态常量
 5  */
 6 public interface MyInterface<T> {
 7     //创建常量
 8     String nameString="金刚大芭比";
 9 
10     //接口方法
11     T server(T t);
12 }
 1 /**
 2  * 实现接口时确定泛型类
 3  */
 4 public class MyInterfaceImpl implements MyInterface<String> {
 5 
 6     @Override
 7     public String server(String s) {
 8         System.out.println(nameString);
 9         System.out.println(s);
10         return s;
11     }
12 }
1 //测试
2 public class Application {
3     public static void main(String[] args) {
4         MyInterfaceImpl myInterface = new MyInterfaceImpl();
5         myInterface.server("xxx");
6     }
7 }
 1 /**
 2  * 实现接口时不确定泛型类
 3  */
 4 public class MyInterfaceImpl2<T> implements MyInterface<T>{
 5     @Override
 6     public T server(T t) {
 7         System.out.println(nameString);
 8         System.out.println(t);
 9         return t;
10     }
11 }
1 //测试
2 public class Application {
3     public static void main(String[] args) {
4         MyInterfaceImpl2<Integer> integerMyInterfaceImpl2 = new MyInterfaceImpl2<>();
5         integerMyInterfaceImpl2.server(20);
6     }
7 }
泛型方法
1 /**
2  * 泛型方法
3  * 语法:<T> 返回类型
4  */
5 public class MyGenericMethod {
6     public <T> void show(T t){
7         System.out.println("泛型方法"+t);
8     }
9 }
1 //测试
2 public class Application {
3     public static void main(String[] args) {
4         MyGenericMethod myGenericMethod = new MyGenericMethod();
5         myGenericMethod.show("金刚");
6         myGenericMethod.show(18);
7     }
8 }
泛型集合
  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
  • 特点:
    • 编译时即可检查,而非运行时抛出异常。
    • 访问时,不必类型转换(拆箱)。
    • 不同泛型指尖引用不能相互赋值,泛型不存在多态。

之前我们在创建LinkedList类型对象的时候并没有使用泛型,但是进到它的源码中会发现:

1 public class LinkedList<E>
2     extends AbstractSequentialList<E>
3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable
4 {//

它是一个泛型类,而我之前使用的时候并没有传递,说明java语法是允许的,这个时候传递的类型是Object类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。


Set集合概述

Set子接口
  • 特点:无序、无下标、元素不可重复。
  • 方法:全部继承自Collection中的方法。
 1 /**
 2  * 测试Set接口的使用
 3  * 特点:1.无序,没有下标;2.重复
 4  * 1.添加数据
 5  * 2.删除数据
 6  * 3.遍历【重点】
 7  * 4.判断
 8  */
 9 public class Demo01 {
10     public static void main(String[] args) {
11         Set<String> set = new HashSet<>();
12         //1.添加数据
13         set.add("火娃");
14         set.add("金娃");
15         set.add("木娃");
16         set.add("水娃");
17         //set.add("水娃");无法添加元素相同数据
18         System.out.println("元素个数为:"+set.size());
19         System.out.println(set.toString());
20 
21         //2.删除数据
22         set.remove("水娃");
23         System.out.println("删除后:"+set.size());
24         System.out.println(set.toString());
25 
26         //3.遍历
27         //3.1 使用增强for循环
28         for (String s:set) {
29             System.out.println(s);
30         }
31         //3.2 使用迭代器
32         Iterator<String> iterator = set.iterator();
33         while (iterator.hasNext()){
34             System.out.println(iterator.next());
35         }
36 
37         //4. 判断
38         System.out.println(set.isEmpty());//false
39         System.out.println(set.contains("火娃"));//true
40     }
41 }

Set实现类

HashSet【重点】
  • 基于HashCode计算元素存放位置。
  • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
 1 /**
 2  * 人类
 3  */
 4 public class Person {
 5     private String name;
 6     private int age;
 7 
 8     public Person(String name, int age) {
 9         this.name = name;
10         this.age = age;
11     }
12 
13     public Person() {
14     }
15 
16     public String getName() {
17         return name;
18     }
19 
20     public void setName(String name) {
21         this.name = name;
22     }
23 
24     public int getAge() {
25         return age;
26     }
27 
28     public void setAge(int age) {
29         this.age = age;
30     }
31 
32     @Override
33     public String toString() {
34         return "Person{" +
35                 "name='" + name + '\'' +
36                 ", age=" + age +
37                 '}';
38     }
39 }
 1 /**
 2  * HashSet集合的使用
 3  * 存储结构:哈希表(数组+链表+红黑树)
 4  * 1.添加元素
 5  * 2.删除元素
 6  * 3.遍历
 7  * 4.判断
 8  */
 9 public class Demo02 {
10     public static void main(String[] args) {
11         HashSet<Person> hashSet=new HashSet<>();
12         Person p1=new Person("tang",21);
13         Person p2=new Person("he", 22);
14         Person p3=new Person("yu", 21);
15         //1.添加元素
16         hashSet.add(p1);
17         hashSet.add(p2);
18         hashSet.add(p3);
19         //重复,添加失败
20         hashSet.add(p3);
21         //直接new一个相同属性的对象,依然会被添加,不难理解。
22         //假如相同属性便认为是同一个对象,怎么修改?
23         hashSet.add(new Person("yu", 21));
24         System.out.println(hashSet.toString());
25         //2.删除元素
26         hashSet.remove(p2);
27         //3.遍历
28         //3.1 增强for
29         for (Person person : hashSet) {
30             System.out.println(person);
31         }
32         //3.2 迭代器
33         Iterator<Person> iterator=hashSet.iterator();
34         while (iterator.hasNext()) {
35             System.out.println(iterator.next());
36         }
37         //4.判断
38         System.out.println(hashSet.isEmpty());
39         //直接new一个相同属性的对象结果输出是false,不难理解。
40         //注:假如相同属性便认为是同一个对象,该怎么做?
41         System.out.println(hashSet.contains(new Person("tang", 21)));
42     }
43 }

注:hashSet存储过程:

  1. 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
  2. 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。

存储过程实际上就是重复依据,要实现“注”里的问题,可以重写hashCode和equals代码:

 1 @Override
 2 public int hashCode() {
 3     final int prime = 31;
 4     int result = 1;
 5     result = prime * result + age;
 6     result = prime * result + ((name == null) ? 0 : name.hashCode());
 7     return result;
 8 }
 9 @Override
10 public boolean equals(Object obj) {
11     if (this == obj)
12         return true;
13     if (obj == null)
14         return false;
15     if (getClass() != obj.getClass())
16         return false;
17     Person other = (Person) obj;
18     if (age != other.age)
19         return false;
20     if (name == null) {
21         if (other.name != null)
22             return false;
23     } else if (!name.equals(other.name))
24         return false;
25     return true;
26 }

 

hashCode方法里为什么要使用31这个数字大概有两个原因:

  1. 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
  2. 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。

TreeSet
  • 基于排序顺序实现不重复。
  • 实现了SortedSet接口,对集合元素自动排序。
  • 元素对象的类型必须实现Comparable接口,指定排序规则。
  • 通过CompareTo方法确定是否为重复元素。
 1 /**
 2  * 使用TreeSet保存数据
 3  * 存储结构:红黑树
 4  * 要求:元素类必须实现Comparable接口,compareTo方法返回0,认为是重复元素
 5  */
 6 public class Demo01 {
 7     public static void main(String[] args) {
 8 
 9         TreeSet<Person> treeSet = new TreeSet<>();
10 
11         Person p1 = new Person("it",12);
12         Person p2 = new Person("he",12);
13         Person p3 = new Person("she",12);
14 
15         //1.添加元素
16         treeSet.add(p1);
17         treeSet.add(p2);
18         treeSet.add(p3);
19         treeSet.add(p3);//相同无法添加
20         //注:直接添加会报类型错误,需要实现Comparable接口
21         System.out.println("元素个数为:"+treeSet.size());
22         System.out.println(treeSet.toString());
23 
24         //2.删除元素
25         treeSet.remove(p3);
26         treeSet.remove(new Person("she",12));
27         System.out.println(treeSet.toString());
28 
29         //3.遍历
30         //3.1 增强for循环
31         for (Person person:treeSet) {
32             System.out.println(person);
33         }
34         //3.2 迭代器
35         Iterator<Person> iterator = treeSet.iterator();
36         while (iterator.hasNext()){
37             System.out.println(iterator.next());
38         }
39         
40         //4.判断
41         System.out.println(treeSet.contains(new Person("it",12)));
42     }
43 }

 运行报错,因为treeset是红黑树,当前代码没有定义比较的属性,需要重写Comparable方法

Java集合 _ JavaClub全栈架构师技术笔记

先查看Comparable接口的源码,发现只有一个compareTo抽象方法,在Person类中实现它:

Java集合 _ JavaClub全栈架构师技术笔记

1 public class Person implements Comparable<Person> {
2     @Override
3     public int compareTo(Person o) {
4         int n1 = this.getName().compareTo(o.getName());//名字相同,返回0,不同返回其他数字
5         int n2 = this.getAge()-o.getAge();//比较年龄
6         return n1==0?n2:n1;//名字相同,返回n2,名字不同,返回n1
7     }
8 }

再次运行TreeSet的Demo01,运行成功

Java集合 _ JavaClub全栈架构师技术笔记

Java集合 _ JavaClub全栈架构师技术笔记

除了实现Comparable接口里的比较方法,还有另外一种方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:

 1 /**
 2  * TreeSet的使用
 3  * Comparator:实现定制比较(比较器)
 4  */
 5 public class Demo02 {
 6     public static void main(String[] args) {
 7         TreeSet<Person> person = new TreeSet<Person>(new Comparator<Person>() {
 8             @Override
 9             public int compare(Person o1, Person o2) {
10                 // 先按年龄比较
11                 // 再按姓名比较
12                 int n1=o1.getAge()-o2.getAge();
13                 int n2=o1.getName().compareTo(o2.getName());
14                 return n1==0?n2:n1;
15             }
16         });
17         Person p1 = new Person("it",12);
18         Person p2 = new Person("he",12);
19         Person p3 = new Person("she",12);
20 
21         //1.添加元素
22         person.add(p1);
23         person.add(p2);
24         person.add(p3);
25         //注:直接添加会报类型错误,需要实现Comparable接口
26         System.out.println("元素个数为:"+person.size());
27         System.out.println(person.toString());
28     }
29 }

Java集合 _ JavaClub全栈架构师技术笔记

接下来我们来做一个小案例:

 1 /**
 2  * 要求:使用TreeSet集合实现字符串按照长度进行排序
 3  * helloworld tangrui hechengyang wangzixu yuguoming
 4  * Comparator接口实现定制比较
 5  */
 6 public class Demo03 {
 7     public static void main(String[] args) {
 8         TreeSet<String> s = new TreeSet<>(new Comparator<String>() {
 9             @Override
10             //1.先比较长度
11             //2.在比较字符串
12             public int compare(String o1, String o2) {
13                 int n1 = o1.length()-o2.length();
14                 int n2 = o1.compareTo(o2);
15                 return n1==0?n2:n1;
16             }
17         });
18         s.add("hello world");
19         s.add("hello Java");
20         s.add("hello Python");
21         System.out.println(s.toString());
22     }
23 }

Java集合 _ JavaClub全栈架构师技术笔记

 

 


Map体系集合

  • Map接口的特点:

    1. 用于存储任意键值对(Key-Value)。
    2. 键:无序、无下标、不允许重复(唯一)。
    3. 值:无序、无下标、允许重复。

Map集合概述

  • 特点:存储一对数据(Key-Value),无序、无下标,键不可重复。

  • 方法:

    • V put(K key,V value)//将对象存入到集合中,关联键值。key重复则覆盖原值。
  • Object get(Object key)//根据键获取相应的值。

    • Set<K>//返回所有的key
    • Collection<V> values()//返回包含所有值的Collection集合。
    • Set<Map.Entry<K,V>>//键值匹配的set集合
 1 /**
 2  * Map接口的使用
 3  * 特点:1.存储键值对 2.键不能重复,值可以重复 3.无序
 4  */
 5 public class Demo01 {
 6     public static void main(String[] args) {
 7         //创建Map集合
 8         HashMap<String, String> map = new HashMap<>();
 9 
10         //1.添加元素
11         map.put("cn","中国");
12         map.put("uk","英国");
13         map.put("usa","美国");
14         map.put("cn","Chinse");//相同key添加,会替换之前的value值
15         System.out.println("元素个数为:"+map.size());
16         System.out.println(map.toString());
17 
18         //2.删除
19         map.remove("usa");
20         System.out.println("删除后元素个数为:"+map.size());
21         System.out.println(map.toString());
22 
23         //3.遍历
24         //3.1 使用keyset(),先获取key,再使用get(key)方法获取value值
25         Set<String> keySet = map.keySet();
26         for (String key:keySet) {
27             System.out.println(key+"-----"+map.get(key));
28         }
29         //简写
30         for (String s:map.keySet()) {
31             System.out.println(s+"-----"+map.get(s));
32         }
33         //3.2 使用entrySet()方法;效率更高,把key和value都拿出来了,不需要为了value再去多做一遍查询key的操作
34         Set<Map.Entry<String, String>> entries = map.entrySet();
35         for (Map.Entry<String,String> entry:entries) {
36             System.out.println(entry.getKey()+"------"+entry.getValue());
37         }
38         //简写
39         for (Map.Entry<String,String> entry:map.entrySet()) {
40             System.out.println(entry.getKey()+"------"+entry.getValue());
41         }
42 
43 
44     }
45 }

keySet遍历和entrySet的区别

Java集合 _ JavaClub全栈架构师技术笔记

 


Map集合的实现类

HashMap【重点】

  • JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。

 1 /**
 2  * 学生类
 3  */
 4 public class Student {
 5     private String name;
 6     private int age;
 7 
 8     public String getName() {
 9         return name;
10     }
11 
12     public void setName(String name) {
13         this.name = name;
14     }
15 
16     public int getAge() {
17         return age;
18     }
19 
20     public void setAge(int age) {
21         this.age = age;
22     }
23 
24     @Override
25     public String toString() {
26         return "Student{" +
27                 "name='" + name + '\'' +
28                 ", age=" + age +
29                 '}';
30     }
31 
32     public Student(String name, int age) {
33         this.name = name;
34         this.age = age;
35     }
36 }
 1 /**
 2  * HashMap的使用
 3  * 存储结构:哈希表(数组+链表+红黑树)
 4  */
 5 public class Demo01 {
 6     public static void main(String[] args) {
 7         HashMap<Student, String> hashMap = new HashMap<>();
 8         Student p1 = new Student("赵一",11);
 9         Student p2 = new Student("王二",11);
10         Student p3 = new Student("张三",11);
11         Student p4 = new Student("李四",11);
12         //1.添加元素
13         hashMap.put(p1,"北京");
14         hashMap.put(p2,"上海");
15         hashMap.put(p3,"广州");
16         hashMap.put(p4,"曹县");
17         hashMap.put(p4,"曹县(山东)");//不能添加重复的key,但是会将value更新
18 
19         //👇,上面已经有一个李四的key了,通过这个还可以继续添加,那能不能让系统根据key的数据判重,而不是根据内存地址?
20         hashMap.put(new Student("李四",11),"南京");
21         System.out.println("元素个数为:"+hashMap.size());
22         System.out.println(hashMap.toString());
23 
24         //2.删除元素
25         hashMap.remove(p2);
26         System.out.println(hashMap.toString());
27 
28         //3.遍历
29         //3.1 使用keySet遍历
30         for (Student s:hashMap.keySet()) {
31             System.out.println(s+"------"+hashMap.get(s));
32         }
33         //3.2 使用entrySet遍历
34         for (Map.Entry<Student,String> entry:hashMap.entrySet()) {
35             System.out.println(entry.getKey()+"------"+entry.getValue());
36         }
37 
38         //4.判断
39         System.out.println(hashMap.containsKey(p1));
40         System.out.println(hashMap.containsKey(new Student("李四",11)));
41         System.out.println(hashMap.containsValue("曹县(山东)"));
42     }
43 }

 

  • 注:和之前说过的HashSet类似,重复依据是hashCode和equals方法,重写即可:

 1 @Override
 2   public int hashCode() {
 3       final int prime = 31;
 4       int result = 1;
 5       result = prime * result + id;
 6       result = prime * result + ((name == null) ? 0 : name.hashCode());
 7       return result;
 8   }
 9   @Override
10   public boolean equals(Object obj) {
11       if (this == obj)
12           return true;
13       if (obj == null)
14           return false;
15       if (getClass() != obj.getClass())
16           return false;
17       Student other = (Student) obj;
18       if (id != other.id)
19           return false;
20       if (name == null) {
21           if (other.name != null)
22               return false;
23       } else if (!name.equals(other.name))
24           return false;
25       return true;
26   }

 

HashMap源码分析

  • 默认初始化容量:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

    • 数组最大容量:static final int MAXIMUM_CAPACITY = 1 << 30;
  • 默认加载因子:static final float DEFAULT_LOAD_FACTOR = 0.75f;

  • 链表调整为红黑树的链表长度阈值(JDK1.8):static final int TREEIFY_THRESHOLD = 8;

  • 红黑树调整为链表的链表长度阈值(JDK1.8):static final int UNTREEIFY_THRESHOLD = 6;

  • 链表调整为红黑树的数组最小阈值(JDK1.8):static final int MIN_TREEIFY_CAPACITY = 64;

  • HashMap存储的数组:transient Node<K,V>[] table;

  • HashMap存储的元素个数:transient int size;
  • 默认加载因子是什么?
    • 就是判断数组是否扩容的一个因子。假如数组容量为100,如果HashMap的存储元素个数超过了100*0.75=75,那么就会进行扩容。
  • 链表调整为红黑树的链表长度阈值是什么?
    • 假设在数组中下标为3的位置已经存储了数据,当新增数据时通过哈希码得到的存储位置又是3,那么就会在该位置形成一个链表,当链表过长时就会转换成红黑树以提高执行效率,这个阈值就是链表转换成红黑树的最短链表长度;
  • 红黑树调整为链表的链表长度阈值是什么?
    • 当红黑树的元素个数小于该阈值时就会转换成链表。
  • 链表调整为红黑树的数组最小阈值是什么?
    • 并不是只要链表长度大于8就可以转换成红黑树,在前者条件成立的情况下,数组的容量必须大于等于64才会进行转换。

HashMap的数组table存储的就是一个个的Node<K,V>类型,很清晰地看到有一对键值,还有一个指向next的指针(以下只截取了部分源码):

1 static class Node<K,V> implements Map.Entry<K,V> {
2       final K key;
3       V value;
4       Node<K,V> next;
5   }

之前的代码中在new对象时调用的是HashMap的无参构造方法,进入到该构造方法的源码查看一下:

1 public HashMap() {
2       this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
3   }

发现没什么内容,只是赋值了一个默认加载因子;而在上文我们观察到源码中table和size都没有赋予初始值,说明刚创建的HashMap对象没有分配容量,并不拥有默认的16个空间大小,这样做的目的是为了节约空间,此时table为null,size为0。

当我们往对象里添加元素时调用put方法:

1 public V put(K key, V value) {
2       return putVal(hash(key), key, value, false, true);
3   }

put方法把key和value传给了putVal,同时还传入了一个hash(Key)所返回的值,这是一个产生哈希值的方法,再进入到putVal方法(部分源码):

 1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
 2                     boolean evict) {
 3       Node<K,V>[] tab; Node<K,V> p; int n, i;
 4       if ((tab = table) == null || (n = tab.length) == 0)
 5           n = (tab = resize()).length;
 6       if ((p = tab[i = (n - 1) & hash]) == null)
 7           tab[i] = newNode(hash, key, value, null);
 8       else{
 9           //
10       }
11   }

这里面创建了一个tab数组和一个Node变量p,第一个if实际是判断table是否为空,而我们现在只关注刚创建HashMap对象时的状态,此时tab和table都为空,满足条件,执行内部代码,这条代码其实就是把resize()所返回的结果赋给tab,n就是tab的长度,resize顾名思义就是重新调整大小。查看resize()源码(部分):

 1 final Node<K,V>[] resize() {
 2       Node<K,V>[] oldTab = table;
 3       int oldCap = (oldTab == null) ? 0 : oldTab.length;
 4       int oldThr = threshold;
 5       if (oldCap > 0);
 6       else if (oldThr > 0);
 7       else {               // zero initial threshold signifies using defaults
 8           newCap = DEFAULT_INITIAL_CAPACITY;
 9           newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
10       } 
11       @SuppressWarnings({"rawtypes","unchecked"})
12       Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
13       table = newTab;
14       return newTab;
15   }

该方法首先把table及其长度赋值给oldTab和oldCap;threshold是阈值的意思,此时为0,所以前两个if先不管,最后else里newCap的值为默认初始化容量16;往下创建了一个newCap大小的数组并将其赋给了table,刚创建的HashMap对象就在这里获得了初始容量。然后我们再回到putVal方法,第二个if就是根据哈希码得到的tab中的一个位置是否为空,为空便直接添加元素,此时数组中无元素所以直接添加。至此HashMap对象就完成了第一个元素的添加。当添加的元素超过16*0.75=12时,就会进行扩容:

1 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict){
2       if (++size > threshold)
3           resize();
4   }

扩容的代码如下(部分):

1 final Node<K,V>[] resize() {
2       int oldCap = (oldTab == null) ? 0 : oldTab.length;
3       int newCap;
4       if (oldCap > 0) {
5           if (oldCap >= MAXIMUM_CAPACITY) {//略}
6           else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
7                    oldCap >= DEFAULT_INITIAL_CAPACITY)
8       }
9   }

核心部分是else if里的移位操作,也就是说每次扩容都是原来大小的两倍。

  • *注**:额外说明的一点是在JDK1.8以前链表是头插入,JDK1.8以后链表是尾插入。


HashSet源码分析

了解完HashMap之后,再回过头来看之前的HashSet源码,为什么放在后面写你们看一下源码就知道了(部分):

 1 public class HashSet<E>
 2       extends AbstractSet<E>
 3       implements Set<E>, Cloneable, java.io.Serializable
 4   {
 5       private transient HashMap<E,Object> map;
 6       private static final Object PRESENT = new Object();
 7       public HashSet() {
 8           map = new HashMap<>();
 9       }
10   }

可以看见HashSet的存储结构就是HashMap,那它的存储方式是怎样的呢?可以看一下add方法:

1 public boolean add(E e) {
2       return map.put(e, PRESENT)==null;
3   }

很明了地发现它的add方法调用的就是map的put方法,把元素作为map的key传进去的。。

Hashtable

  • JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。
  • 初始容量11,加载因子0.75。

    这个集合在开发过程中已经不用了,稍微了解即可。

Properties

  • Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。

它继承了Hashtable的方法,与流关系密切,此处不详解。

TreeMap

  • 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
 1 /**
 2  * TreeMap的使用
 3  * 存储结构:红黑树
 4  */
 5 public class Demo3 {
 6     public static void main(String[] args) {
 7         TreeMap<Student, Integer> treeMap=new TreeMap<Student, Integer>();
 8         Student s1=new Student("tang", 36);
 9         Student s2=new Student("yu", 101);
10         Student s3=new Student("he", 10);
11         //1.添加元素
12         treeMap.put(s1, 21);
13         treeMap.put(s2, 22);
14         treeMap.put(s3, 21);
15         //不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小
16         System.out.println(treeMap.toString());
17         //2.删除元素
18         treeMap.remove(new Student("he", 10));
19         System.out.println(treeMap.toString());
20         //3.遍历
21         //3.1 使用keySet()
22         for (Student key : treeMap.keySet()) {
23             System.out.println(key+" "+treeMap.get(key));
24         }
25         //3.2 使用entrySet()
26         for (Entry<Student, Integer> entry : treeMap.entrySet()) {
27             System.out.println(entry.getKey()+" "+entry.getValue());
28         }
29         //4.判断
30         System.out.println(treeMap.containsKey(s1));
31         System.out.println(treeMap.isEmpty());        
32     }
33 }

在学生类中实现Comparable接口:

1 public class Student implements Comparable<Student>{
2     @Override
3     public int compareTo(Student o) {
4         int n1=this.id-o.id;
5         return n1;
6 }

除此之外还可以使用比较器来定制比较:

1 TreeMap<Student, Integer> treeMap2=new TreeMap<Student, Integer>(new Comparator<Student>() {
2     @Override
3     public int compare(Student o1, Student o2) {
4         //
5         return 0;
6     }            
7 });

 

TreeSet源码

和HashSet类似,放在TreeMap之后讲便一目了然(部分):

 1 public class TreeSet<E> extends AbstractSet<E>
 2     implements NavigableSet<E>, Cloneable, java.io.Serializable
 3 {
 4     private transient NavigableMap<E,Object> m;
 5     private static final Object PRESENT = new Object();
 6     TreeSet(NavigableMap<E,Object> m) {
 7         this.m = m;
 8     }
 9     public TreeSet() {
10         this(new TreeMap<E,Object>());
11     }
12 }

TreeSet的存储结构实际上就是TreeMap,再来看其存储方式:

1 public boolean add(E e) {
2     return m.put(e, PRESENT)==null;
3 }

它的add方法调用的就是TreeMap的put方法,将元素作为key传入到存储结构中。


Collections工具类

  • 概念:集合工具类,定义了除了存取以外的集合常用方法。

  • 方法:

    • public static void reverse(List<?> list)//反转集合中元素的顺序
    • public static void shuffle(List<?> list)//随机重置集合元素的顺序
    • public static void sort(List<T> list)//升序排序(元素类型必须实现Comparable接口)
 1 /**
 2  * 演示Collections工具类的使用
 3  *
 4  */
 5 public class Demo4 {
 6     public static void main(String[] args) {
 7         List<Integer> list=new ArrayList<Integer>();
 8         list.add(20);
 9         list.add(10);
10         list.add(30);
11         list.add(90);
12         list.add(70);
13         
14         //sort排序
15         System.out.println(list.toString());
16         Collections.sort(list);
17         System.out.println(list.toString());
18         System.out.println("---------");
19         
20         //binarySearch二分查找
21         int i=Collections.binarySearch(list, 10);
22         System.out.println(i);
23         
24         //copy复制
25         List<Integer> list2=new ArrayList<Integer>();
26         for(int i1=0;i1<5;++i1) {
27             list2.add(0);
28         }
29         //该方法要求目标元素容量大于等于源目标
30         Collections.copy(list2, list);
31         System.out.println(list2.toString());
32         
33         //reserve反转
34         Collections.reverse(list2);
35         System.out.println(list2.toString());
36         
37         //shuffle 打乱
38         Collections.shuffle(list2);
39         System.out.println(list2.toString());
40         
41         //补充:list转成数组
42         Integer[] arr=list.toArray(new Integer[0]);
43         System.out.println(arr.length);
44         //补充:数组转成集合 
45         String[] nameStrings= {"tang","he","yu"};
46         //受限集合,不能添加和删除
47         List<String> list3=Arrays.asList(nameStrings);
48         System.out.println(list3);
49         
50         //注:基本类型转成集合时需要修改为包装类
51     }
52 }

 

作者:葛老头
来源链接:https://www.cnblogs.com/gltou/p/15402087.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/17535.html

标签: Java集合Java
分享给朋友:

“Java集合” 的相关文章

Java空指针异常解决java.lang.NullPointerException解决心得

Java空指针异常解决java.lang.NullPointerException解决心得

今天做课设的时候运行程序报出以下错误 java.lang.NullPointerException 首先要理解的是此错误并不会在 程序中报错,只会在运行的时候报错。 是由于某个参数(集合,数组等数据)可能出现一个null值而导致后面的程序不能运行时...

Java 并发核心机制

Java 并发核心机制

📦 本文以及示例源码已归档在 javacore 一、J.U.C 简介 Java 的 java.util.concurrent 包(简称 J.U.C)中提供了大量并发工具类,是 Java 并发能力的主要体现(注意,不是全部,有部分并发能力的支持在其他包中)。...

Java 日志框架详解

Java 日志框架详解

1. JUL学习 JUL全称Java util Logging是java原生的日志框架,使用时不需要另外引用第三方类库,相对其他日志框 架使用方便,学习简单,能够在小型应用中灵活使用。 1.1 架构介绍 Loggers...

JAVA的JDK环境变量的配置JAVA

JAVA的JDK环境变量的配置JAVA

首先要在官网下载java 官网:http://www.oracle.com/technetwork/java/javase/downloads/ 到这个界面 选择我接受 记住该地址 最好的办法新建记事本,然后按ctrl+s保存 java环境变量的...

在JAVA 中将堆与栈分开的原因

栈是运行时的单位,而堆是存储的单位。 栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么 放、放在哪儿。 注意:在Java中一个线程就会相应有一个线程栈与之对应 栈因为是运行单位,因此里面存储的信息都是跟...

全网最细笔记java与kotlin的一些异同

本文主要介绍java与kotlin的一些异同 后面可能还会继续比较kotlin和dart 期待吗? 打印日志 Java System.out.print("Amit Shekhar"); S...

JAVA UUID 生成唯一标识

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket Reprint it anywhere u want 需求     项目在设计表的时候,要处理并发多...

初探设计:Java继承何时用?怎么用?

初探设计:Java继承何时用?怎么用?

Writer      :BYSocket(泥沙砖瓦浆木匠) 一、回顾继承 常见的如下: 1、依赖(”uses-a“) 2、聚合(”has-a“) 3、继承(”is-a“)类...

编写高质量代码改善java程序的151个建议——[52

编写高质量代码改善java程序的151个建议——[52

原创地址:   http://www.cnblogs.com/Alandre/  (泥沙砖瓦浆木匠),需要转载的,保留下! Thanks Although the world is full of...

图解 Java IO : 二、FilenameFilter源码

图解 Java IO : 二、FilenameFilter源码

Writer      :BYSocket(泥沙砖瓦浆木匠) 微         博:BYSocket 豆  &...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。