Java集合框架总结

时间:2023-02-17 16:32:04

首先说一下集合的由来:

    java中我们存储比较大量的数据的时候,我们一般用的是数组,但是有一种一下几种情况我们使用数组的话就比较麻烦了:

    1.当我们不知道我们的数据到底有多少的时候,(一般我们会数值一个比较大的数组这样就造成了内存的浪费)

    2.当我们向一个满的数组继续放入数据的时候,会出现越界问题,如果想继续存入我们必须将这个数组拷贝到一个更大的数组中。因此java中定义了集合,集合最最基本的特点就是:可以自动增加。当然集合还有其他特点。

  集合的几个类型:list,set,map主要的就这三个。

   集合和数组的区别:

 1.   数组可以存储基本数据类型,也可以存储应用数据类型,存储基本数据类型的时候存储的是值,存储引用数据类型的时候存储的是地址值;

      集合只能存储引用数据类型,(当存储基本数据类型的时候他会自动装箱比如int--->Integer char --->Character.....)

2.  数组长度不可变,而且存储数据的时候类型在定义的时候已经限制了

    集合长度可变,而且因为所有的引用数据类型都有一个父类(object)因此集合可以放入多种引用数据类型。


各种集合继承关系:

Java集合框架总结

这一点要注意,虽然都是集合但是他们的底层是不一样的。

下面开始了解一下List:

List的定义:标准的定义是
  List<E> list=new List的子类<>();
(E表示list里面放的类型,就行数组前面的类型一样,注意这里不能写int这些只能写Integer) 
  当然亦可以不写E
  List list=new Lis的子类t();那么他会默认E=object

List子类共有的方法:

 

                   set(int index, E element)//在指定位置增加元素
               boolean add(E e)         //增加元素
		boolean remove(Object o) //删除元素
		void clear()              //清空
		boolean contains(Object o)//!!很有用的一个函数,可以确定一个元素是否在集合中存在
		boolean isEmpty()         //判断是否没空
		int size()               //打到尺寸 

当然还有其他方法,可以看API

  接下来说说各个集合的特点吧,先来介绍List的各个子类:

 ArrayList:    最常用的一个子类,里面的数据可以重复怎么存入就怎么读写。底层的数据结构是数组,查询快,增删慢,线程不安全,效率高。

  Vector:   底层也是数组,查询快,增删慢。线程安全,效率低;

LinkedList: 底层数据结构是链表,查询慢,增删快。线程不安全,效率高。这个一般不经常用。只用于大量数据增删的情况。

来解释一下线程不安全的与线程安全的大致意思吧:

  假设这里有一种场景,有一个ArrayList(),然后有两个并行的线程同时对这个List进行操作,那么就会出现一种情况,线程A删了一个元素,这时候线程B想知道这个List大小的时候,就会出现问题,或许线程B中的List还没有更新,这样就导致了错误,这就是线程不安全,而Vector理论上是线程安全的。(实际上并不是的,还是会出现并发异常的问题.....怎么体现Vector并不是很安全参见https://blog.csdn.net/zlp1992/article/details/50433778)

然后介绍一些LinkedList中的一些特有方法。

        * public void addFirst(E e)及addLast(E e)            //在开头或者结尾增加元素
	* public E getFirst()及getLast()                    //得到开头、结尾
	* public E removeFirst()及public E removeLast()     //移除开头或者结尾

因为LinkedList的底层是链表,因此很容易定位到开头和结尾


集合的第二大类:Set

set相比于list的不同:

1.list是有序的,set是无序的。即list会根据你的输入打印,打算set不会。

2.list可以放入重复的元素,但是set不能。----这也是他无序的原因,他将每个元素赋予了一个哈希值这样就保证了元素的不重复。

(但是有一点要注意,他不能比较两个类是否相同)


比如:我们又一个类 Person( name,age) 要放入set中

                        HashSet<Person> hs = new HashSet<>();
			hs.add(new Person("张三", 23));
			hs.add(new Person("张三", 23));

这样两个Person都会放入,尽管我们认为这两个类是一样的,但是hashCode函数并不会认为他们是同一个对象,因此还是都会存入set,这时候我们必须重写hashCode函数和equal()函数:

为了加深理解我们用代码解释一下:

public class Person {
	private String name;
	private int age;
	public Person() {
		super();
		
	}
	public Person(String name, int age) {
		super();
		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 hashCode() {
		System.out.println("asdasd");
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		System.out.println("2112");
		if (this == obj)						//调用的对象和传入的对象是同一个对象
			return true;						//直接返回true
		if (obj == null)						//传入的对象为null
			return false;						//返回false
		if (getClass() != obj.getClass())		//判断两个对象对应的字节码文件是否是同一个字节码
			return false;						//如果不是直接返回false
		Person other = (Person) obj;			//向下转型
		if (age != other.age)					//调用对象的年龄不等于传入对象的年龄
			return false;						//返回false
		if (name == null) {						//调用对象的姓名为null
			if (other.name != null)				//传入对象的姓名不为null
				return false;					//返回false
		} else if (!name.equals(other.name))	//调用对象的姓名不等于传入对象的姓名
			return false;						//返回false
		return true;							//返回true
	}
}

这里有一个Person类

public class Demo1_HashSet {

	/**
	 * @param args
	 * Set集合,无索引,不可以重复,无序(存取不一致)
	 */
	public static void main(String[] args) {
		//demo1();
		HashSet<Person> hs = new HashSet<>();
		hs.add(new Person("张三", 23));
		hs.add(new Person("张三", 23));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		hs.add(new Person("李四", 24));
		
		//System.out.println(hs.size());
		System.out.println(hs);
       }
	}

重写hashCode函数是防止出现重名的对象;重写equals函数可以防止真的对象的重复,注释已经很详细了我就不在解释了。


然后是

LinkedHashSet:

  因为set是无序的为了解决这个缺点我们使用了linkeHashSet,大家都知道链表是有序的,所以这个集合是有序切不能重复的。这个特点可以用来去除重复,比如“aaabbssdd”最后存进去的可以变成absd;

然后是最后一个TreeSet;

他的底层也有哈希函数,但是多了一个树!!那么他就有了一个新的特性,就是会自动排序,底层会很久哈希值对所有的元素排序。

   当然:如果想认为的设置这个的顺序,需要重写compareTo函数,为什么会出现这样的情况呢?还是由于之前的问题,对于我们自定义的对象,他不会排序。

    在比较的时候他会调用compareTo函数,这个函数返回值为 -1 0 1  :也就是小于等于大于。当返回1的时候就放在左节点,返回-1的时候就放在右节点;因此才会排序;