2020.11.10——Java集合

一、集合概述:

在这里插入图片描述

  • 集合类存放在java.util包中,是一个用来存放对象的容器。
  • 集合只能存放对象。
  • 集合总存放的是多个对象的引用,对象本身还是存放在堆中。
  • 集合可以存放不同类型的,不限数量的数据类型。

二、Set:

  • HashSet 中的方法最终都是来源于Collection接口。
  • 向Set中添加的数据,其所在的类一定要重写hashCode()和equals(),以实现对象相等的规则。即“相等的对象(即equlas()判断相等)必须具有相等的散列码(即hashCode()相等)” 。
    在这里插入图片描述
    一般直接使用编译器生成的重写方法。(Alt + shift + s)

1、HashSet:

在这里插入图片描述

在这里插入图片描述

HashSet特点:

  • HashSet不是线程安全的。
  • 不能保证元素的排列顺序:根据hashcode值决定对象存储的位置
  • 不可重复:指的是hashcode()返回的值不相等。
  • 集合元素可以存null。
  • HashSet底层:数组+链表 的结构。

添加元素的过程:

在这里插入图片描述
在这里插入图片描述

HashSet几个基本操作:


import java.util.HashSet;
import java.util.Set;

public class Test {
	public static void main(String[] args) {
		Set set = new HashSet();
		
		//添加元素;
		set.add(1);
		set.add("a");
		System.out.println(set);
		
		//移除元素;
		set.remove("a");
		System.out.println(set);
		
		//判断元素是否在集合中,返回布尔值;
		System.out.println(set.contains(1));
		
		//获取集合大小;
       System.out.println(set.size());
       
		//清空;
		set.clear();
		System.out.println(set);
	}
}

HashSet遍历集合方式:

  • 使用迭代器
  • for each 迭代:其实内部还是调用的迭代器。
    在这里插入图片描述
		//使用迭代器遍历集合;
		Iterator it = set.iterator();
		while (it.hasNext()) {
			System.out.println(it.next());
		}
		
		//for each迭代集合;
		for(Object obj: set) {//这个的意思是set的每一个值取出来,赋值给obj,直到循环set的所有值
			System.out.println(obj);
		}

泛型:

//使用泛型;即让集合存储指定类型的元素;Object类型的其实也就是所有可以存储的类型,下面两行是等价的。
		Set set = new HashSet();
		Set<Object> set1 = new HashSet<Object>();
		
		Set<String> set2 = new HashSet<String>();//指定类型为String

2.LinkedHashSet:

LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据(其实就是双向链表)。
优点:对于频繁的遍历操作,可以按照添加的顺序遍历,LinkedHashSet效率高于HashSet。

3.TreeSet:

在这里插入图片描述

TreeSet特点:

  • 自然排序 、定制排序。
  • treeset必须放入同类型的对象。(使用泛型来限制)
  • TreeSet和TreeMap采用红黑树的存储结构。
    在这里插入图片描述
    自然排序根据需求去重写compareTo()方法即可。
    注意:自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()。
    在这里插入图片描述
    注意:定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。

定制排序例子:

例一:

import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

public class Test1 {
	public static void main(String[] args) {
		
		Person p1 = new Person(14,"ais");
		Person p2 = new Person(17,"yukino");
		Person p3 = new Person(20,"asuna");
		Person p4 = new Person(16,"makisei");
		
		Set<Person> set = new TreeSet<Person>(new Person());//new一个TreeSet对象,存储Person类型的对象
		set.add(p1);
		set.add(p2);
		set.add(p3);
		set.add(p4);
		
		for (Person person : set) {
			System.out.println(person.name + ":" + person.age);
		}
		
	}
}

class Person implements Comparator<Person>{
	 int age;
	 String name;
	 
	 public Person() {//无参构造器
		 
	 }
	 public Person(int age, String name) {//有参构造
		 this.age = age;
		 this.name = name;
	 }
	 
	 @Override
	public int compare(Person o1, Person o2) {//按年龄进行排序
		if (o1.age < o2.age) {
			return 1;
		}else if (o1.age > o2.age) {
			return -1;
		}else {
			return 0;
		}
		
	}
	

例二:传入参数的定制排序。

package review;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest {

	public static void main(String[] args) {
		sort();
	}
	
	public static void sort() {
		Comparator compare = new Comparator() {

			@Override
			public int compare(Object o1, Object o2) {
				if(o1 instanceof User && o2 instanceof User) {
					User u1 = (User)o1;
					User u2 = (User)o2;
					return Integer.compare(u1.getAge(), u2.getAge());
				}else {
					throw new RuntimeException("输入数据类型不匹配");
				}
			}
		};
		
		TreeSet<Object> set = new TreeSet<>(compare);//在这传入了定制排序的参数compare
		set.add(new User("tom", 12));
		set.add(new User("jery", 18));
		set.add(new User("mike", 15));
		set.add(new User("jack", 14));
		
		Iterator<Object> iterator = set.iterator();
		while(iterator.hasNext()) {
			System.out.println(iterator.next());
		}
		
	}

}

TreeSet遍历集合方式:

与hashset相同。

三、List与ArrayList、LinkedList、Vector:

Java ArrayList 菜鸟教程
Java LinkedList 菜鸟教程

简述:

一些操作跟集合相同,并有其额外的方法。(具有索引一类的用法)
在这里插入图片描述
contains(Object obj):判断当前集合中是否包含obj。
注意:在判断时会调用obj对象所在类的equals()方法。
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类重写equals()

拓展:
集合----->数组:toArray()
数组----->集合:调用Arrays类的静态方法Arrays.asList()

例子:

import java.util.ArrayList;
import java.util.List;

public class Test2 {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("a");
		list.add("b");
		list.add("c");
		list.add("d");
		list.add("b");
	    List<String> sublist = list.subList(1, 4);//输出指定范围的元素
		System.out.println(sublist);
	}
}

在这里插入图片描述
面试题:ArrayList、LinkedList、Vector三者的异同?
相同点:三个类都是实现了List接口,存放数据的特点相同:存储有序的,可重复的数据。
不同点:

  • ArrayList:作为List接口的主要实现类;现场不安全,效率高;底层使用Object[]存储
  • LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList效率高;底层使用双向链表存储
  • Vector:作为List的古老实现类;线程安全,效率低;底层使用Object[]存储

ArrayList源码分析:

JDK7情况下:
(1)ArrayList list = new ArrayList();底层创建了长度是10 的Object[]数组elementData。
(2)list.add(obj),如果此次的添加导致底层的elementData数组容量不够,则扩容。默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中
结论:建议开发中使用带参的构造器: ArrayList list = new ArrayLIst(int capacity);

JDK8中:
(1)ArrayList list = new ArrayLIst();底层Object[] elementData初始化为{ },并没有创建长度为10的数组
(2)list.add(obj);在第一次调用add()时,底层才创建了长度为10 的数组,并将数据obj添加到elementData中。后续的添加与扩容操作与jdk 7相同。
结论:JDK7中ArrayList的对象的创建类似于单例的饿汉式,而JDK8中则类似于懒汉式,延迟了数组的创建,节省了内存。

LinkedList源码分析:

内部使⽤双向链表的结构实现存储,LinkedList有⼀个内部类作为存放元素的单元,⾥⾯有三个属性,⽤来存放元素本身以及前后2个单元的引⽤,另外LinkedList内部还有⼀个header属性,⽤来标识起始位置,LinkedList的第⼀个单元和最后⼀个单元都会指向header,因此形成了⼀个双向的链表结构。

Vector源码分析:

扩容方法不同。初始化容量同样为10,默认扩容为原来的2倍

练习:

1.取出List中重复的元素 :


import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

/**
 *取出List中重复的元素 
 *
 */
public class ListDuplicate {

	private static HashSet hashSet;

	public static void main(String[] args) {
		ArrayList<Object> list = new ArrayList<Object>();
		list.add(1);
		list.add(2);
		list.add(5);
		list.add(2);
		list.add(3);
		list.add(4);
		List duplicateList = duplicateList(list);
		duplicateList.forEach(System.out::println); //1,2,3,4,5
	}
	
	public static List duplicateList(List list) {
		HashSet set = new HashSet();
		set.addAll(list);
		return new ArrayList(set);

	}
}

2.判断下列代码输出,Person()类已经重写过equals()方法和hashCode()方法。考察对HashSet数据添加及删除的底层理解。
在这里插入图片描述
在这里插入图片描述
在重写equals()和hashCode()后:修改了元素的属性值,此时修改后对象的hashCode值还是原先的值。在Set中使用remove()删除的时候,根据旧索引可以找到修改后的对象,但是remove()会先计算当前对象的哈希值,根据哈希值找对应的位置;但是,由于修改了属性后,对象的哈希值变了,这会使得remove()在删除的时候找到一个空的对象的位置,所以实质上也就没有删除任何元素。

四、Map:

Map特点:

在这里插入图片描述
在这里插入图片描述
Map是接口,实现类一般为HashMap。
Map结构的理解
(1)map中的key:无序的、不可重复的,使用Set存储所有的key。------> 要求key所在的类药重写equals()和hashCode()。(以HashMap为例)
(2)map中的value:无序的、可重复的,使用Collection存储所有的value;
(3)一个键值对构成了一个Entry对象;
(4)map中的Entry:无序的、不可重复的,使用Set存储所有的Entry。

(1)HashMap:

底层:(1)JDK7及之前:数组+链表(2)JDK8 :数组+链表+红黑树

基本方法:

在这里插入图片描述

import java.util.HashMap;
import java.util.Map;

public class Test3 {
	public static void main(String[] args) {
		
		Map<String, Integer> map = new HashMap<String, Integer>();
		
		map.put("key1", 1);//put写入元素
		map.put("key2", 1);
		map.put("key3", 2);
		map.put("key4", 3);
		
		System.out.println(map);
		
		System.out.println(map.get("key2"));//根据键来取值
		
		//判断是否包含某个键或者某个值
		map.containsKey("key");
		map.containsValue(2);
		
	}
}

map.keySet(); //获取map集合所有的key的集合。
map.value(); //获取集合的所有的value值。

HashMap遍历:

(1)通过获取所有的键来遍历:keySet():返回所有 key构成的Set集合。

public class Test3 {
	public static void main(String[] args) {
		
		Map<String, Integer> map = new HashMap<String, Integer>();
		
		map.put("key1", 1);//put写入元素
		map.put("key2", 1);
		map.put("key3", 2);
		map.put("key4", 3);
		
		//通过获取到所有的键来遍历,此时也可以使用iterator()来遍历
		Set<String> keys = map.keySet();
		for (String key : keys) {
			System.out.println("key:" + key + ",value:" + map.get(key));
		}
	}
}

(2)Collection中的 values():返回所有value构成的Collection集合。

Collection values = map.values();
for(Object obj: values){
	System.out.println(obj);
}

(3)entrySet()遍历:返回所有key-value对 构成的Set集合。

public class Test3 {
	public static void main(String[] args) {
		
		Map<String, Integer> map = new HashMap<String, Integer>();
		
		map.put("key1", 1);//put写入元素
		map.put("key2", 1);
		map.put("key3", 2);
		map.put("key4", 3)//通过entrySet()来遍历, 也可以使用iterator()来遍历。
		Set<Entry<String, Integer>> entrys = map.entrySet();
		for (Entry<String, Integer> entry : entrys) {
			System.out.println("key:" + entry.getKey() + ",value:" + entry.getValue());
		}
	}
}

HashMap的底层实现原理:

(1)JDK7中:
在这里插入图片描述
扩容时:当超出临界值且要存放的位置非空时,扩容,默扩容为原来的2倍。初始容量为16。
threshold:扩容的临界值,= 容量*加载因子: 16 * 0.75 = 12
在这里插入图片描述

(2)JDK8中:
在这里插入图片描述
在这里插入图片描述
无参构造器:默认加载因子0.75,并没有创建数组。
TREEIFY_THRESHOLD:bucket中链表长度大于该默认值,转换为红黑树8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量64

在这里插入图片描述

(2)LinkedHashMap:

保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在HashMap底层结构的基础上,添加了指向前一个元素和后一个元素的两个指针。即:对于频繁的遍历操作,此类执行效率高于HashMap。

(3)HashTable:

在这里插入图片描述

(4)Properties:

Properties类是HashTable的子类,常用来处理配置文件。key和value都是String类型

(5)TreeMap:

  • 一般使用map集合,不会使用过于复杂的对象做key。底层使用红黑树存储
  • 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,因为要按照key进行排序:自然排序、定制排序。排序这里可以参考TreeSet。
    在这里插入图片描述
    TreeMap自然排序:
public class Test4 {
	public static void main(String[] args) {
		
		//TreeMap的自然排序是字典排序
		Map<Integer, String> map = new TreeMap<Integer, String>();
		
		map.put(4, "a");
		map.put(2, "a");
		map.put(5, "a");
		map.put(1, "a");
		System.out.println(map);//{1=a, 2=a, 4=a, 5=a}
		
	}
}

五、操作集合的工具类:Collections

在这里插入图片描述

在这里插入图片描述

  • 一些直接操作:
Collections.reverse(list);//对集合元素进行反转;集合本身被修改了!
Collections.shuffle(list);//对集合元素进行随机排列;
Collections.sort(list);//对集合元素进行升序排列;(根据元素的自然顺序进行降序或者升序排列)
Collections.swap(list, int, int);//将指定list集合中i 处元素和 j 处元素进行交换。
Collections.frequency(c, o);//返回指定集合中指定元素的出现次数。
Collections.replaceAll(list, oldVal, newVal);//使用新值替换list对象的所有旧值。

注意copy()的使用:将原集合复制到一个新的集合中,要要求新的集合的大小大于原集合的大小才能完成复制,否则会抛出异常。一般创建方法如下:

List dest = Arrays.asList(new Object[list.size()]);//一般创建新集合的方法,list是原集合
  • 根据指定的comparator产生的顺序对集合进行排序:
    Collections.sort(stu,new Student()); // sort方法会根据,new Student()的compare重写方法进行排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Collections集合工具类
 * @author Ais Wallenstein
 *
 */
public class Test5 {
	public static void main(String[] args) {
		
		Student s1 = new Student(11,"张三");
		Student s2 = new Student(15,"李四");
		Student s3 = new Student(13,"王五");
		Student s4 = new Student(12,"马六");
		List<Student> stu = new ArrayList<Student>();
		stu.add(s1);
		stu.add(s2);
		stu.add(s3);
		stu.add(s4);
		
		//遍历集合
		for (Student student : stu) {
			System.out.println(student.name + ":" + student.age);
		}
		
		System.out.println("-----------");
		
		//根据指定的comparator产生的顺序对集合进行排序。
		Collections.sort(stu,new Student());
		for (Student student : stu) {
			System.out.println(student.name + ":" + student.age);
		}
	}
}

class Student implements Comparator<Student>{
	 int age;
	 String name;
	 
	 public Student() {//无参构造器
		 
	 }
	 public Student(int age, String name) {//有参构造
		 this.age = age;
		 this.name = name;
	 }
	 
	 @Override
	public int compare(Student o1, Student o2) {//按年龄进行排序
		if (o1.age < o2.age) {
			return 1;
		}else if (o1.age > o2.age) {
			return -1;
		}else {
			return 0;
		}
	}	
}
  • 根据Comparator指定的顺序,返回集合中最大或最小的元素。
Student stu_max = Collections.max(stu, new Student());
		Student stu_min = Collections.min(stu, new Student());
		System.out.println(stu_max.name + ":" + stu_max.age);
		System.out.println(stu_min.name + ":" + stu_min.age);

在这里插入图片描述
举例:

List list1 = Collections.synchronizedList(list);

此时返回的list1就是线程安全的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值