知识点_20190310

 

https://blog.csdn.net/a2011480169/article/details/52047600

在Java当中,如果有一个类专门用来存放其它类的对象,这个类就叫做容器,或者就叫做集合,集合就是将若干性质相同或相近的类对象组合在一起而形成的一个整体。

2、容器与数组的关系
之所以需要容器:
1、数组的长度难以扩充
2、数组中数据的类型必须相同
容器与数组的区别与联系:
1、容器不是数组,不能通过下标的方式访问容器中的元素
2、数组的所有功能通过Arraylist容器都可以实现,只是实现的方式不同
3、如果非要将容器当做一个数组来使用,通过toArray方法返回的就是一个数组
 

boolean add(Object obj):向容器中添加指定的元素
Iterator iterator():返回能够遍历当前集合中所有元素的迭代器
Object[] toArray():返回包含此容器中所有元素的数组。
Object get(int index):获取下标为index的那个元素
Object remove(int index):删除下标为index的那个元素
Object set(int index,Object element):将下标为index的那个元素置为element
Object add(int index,Object element):在下标为index的位置添加一个对象element
Object put(Object key,Object value):向容器中添加指定的元素
Object get(Object key):获取关键字为key的那个对象
int size():返回容器中的元素数

4、容器的分类
容器分为Set集、List列表、Map映射
Set集合:由于内部存储结构的特点,Set集合中不区分元素的顺序(即使存放的类实现了compareTo方法,也是没用的),不允许出现重复的元素(用户自定义的类有的时候需要实现相应方法),TreeSet容器特殊,元素放进去的时候自然而然就有顺序了,Set容器可以与数学中的集合相对应:相同的元素不会被加入。
 

List列表:由于内部存储结构的特点,List集合中区分元素的顺序,且允许包含重复的元素。List集合中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素—有序,可以重复
Map映射:由于内部存储结构的特点,映射中不能包含重复的键值,每个键最多只能映射一个值,否则会出现覆盖的情况(后面的value值会将前面的value值覆盖掉),Map是一种把键对象和值对象进行映射的集合,即Map容器中既要存放数据本身,也要存放关键字:相同的元素会被覆盖
 

6、Comparable接口中的compareTo()方法:凡是需要进行比较排序的类都应该实现Comparable接口中的compareTo()方法;凡是把类对象放到以树为内部结构的容器中都应该实现Comparable接口中的compareTo()方法

7、凡是把类对象放到以哈希表为内部存储结构的容器中,相应的类必须要实现equals方法和hashCode方法,这样才符合哈希表真实的逻辑功能.(对于咱们自己定义的类,如果你没有重写hashcode方法,我们可以通过hashcode方法获取该对象的内存地址)

简而言之:哈希表先根据它的hashcode方法提供的哈希码找到存储的位置,在从位置所关联的链表里面寻找是否有相同的对象,如果有相同的对象,则不存放,如果没有,则存放进去。

8、重要的一个逻辑:逻辑上来讲,只要两个对象的内容相同,其地址(hashCode()返回值)以及这两个对象就应该相同(equals()),

1、凡是把类对象放到容器中,相应的类都应该实现Object类中的toString()方法;
2、凡是需要进行比较排序的类都应该实现Comparable接口中的compareTo()方法;凡是把类对象放到以树为内部结构的容器中都应该实现Comparable接口中的compareTo()方法
3、凡是把类对象放到以哈希表为内部存储结构的容器中,相应的类必须要实现equals方法和hashCode方法,这样才符合哈希表真实的逻辑功能.
4、逻辑上来讲,只要两个对象的内容相同,其地址(hashCode()返回值)以及这两个对象就应该相同(equals())。
 

9、哈希冲突的相关概念

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

public class Test1 {
	public static void main(String[] args) {
		Student s1 = new Student(1,"s1");
		Student s2 = new Student(2,"s2");
		Student s3 = new Student(3,"s3");
		Student s4 = new Student(4,"s4");
		Student s5 = new Student(5,"s5");
		
		/*TreeMap*/
		Map<Student,String> tm = new TreeMap<Student,String>();
		tm.put(s5,"1");
		tm.put(s2,"2");
		tm.put(s1,"3");
		tm.put(s3,"4");
		tm.put(s4,"5");
		System.out.println("tm:  "+ tm);
		
		/*ArrayList*/
		List<Student> al = new ArrayList<Student>();
		al.add(s5);
		al.add(s2);
		al.add(s1);
		al.add(s3);
		al.add(s4);
		Collections.sort(al);
		System.out.println("al: "+ al);
		
		
		List<Student> al1 = new ArrayList<Student>();
		al1.add(s5);
		al1.add(s2);
		al1.add(s1);
		al1.add(s3);
		al1.add(s4);
		Collections.sort(al1, new Comparator<Student>() {
			@Override
			public int compare(Student o1, Student o2) {
				return   o2.getId()-o1.getId();
			}
		});
		System.out.println("al1 "+ al1);
		
		/*HashSet*/
		Set<Student> hs = new HashSet<Student>();
		hs.add(s5);
		hs.add(s2);
		hs.add(s1);
		hs.add(s3);
		hs.add(s4);
		Student s6 = new Student(5,"s5");
		hs.add(s6);
		System.out.println("hs: "+ hs);
		
		/*TreeSet*/
		Set<Student> ts = new TreeSet<Student>();
		ts.add(s5);
		ts.add(s2);
		ts.add(s1);
		ts.add(s3);
		ts.add(s4);
		ts.add(s6);
		System.out.println("ts: "+ ts);
	}
}

class Student implements Comparable<Student>{
	private int id;
	private String name ;
	
	public Student(int id ,String name) {
		this.id = id ;
		this.name = name ;
	}

	
	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + "]";
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}


	@Override
	public int compareTo(Student o) {
		return this.id - o.id;
	}


	public int getId() {
		return id;
	}


	public void setId(int id) {
		this.id = id;
	}


	public String getName() {
		return name;
	}


	public void setName(String name) {
		this.name = name;
	}
	
	
}

图里描述了java容器类库,包括抽象类和遗留的构建,反复看懂有助于理解整个集合框架,图里没包括进去的主要有:

Queue接口及其实现,包括优先权队列PriorityQueue和各种BlockingQueue
ConcurrentMap接口及其实现ConcurrentHashMap
CopyOnWriteArrayList和CopyOnWriteArraySet
为使用enum而提供的Set和Map的特殊实现,EnumSet和EnumMap

ArrayList是顺序容器,底层通过数组实现,允许放入null值。每个ArrayList都有一个容量capacity,表示底层实现数组的大小,当添加元素的时候,如果capacity不够,会自动增加数组的大小。

对于ArrayList而言,size(),isEmpty(),get(),set()方法的时间复杂度是常数时间,add()方法开销和插入的位置有关,addAll()方法开销和添加的元素数量成正比,其余方法都是线性时间完成。
 

HashMap 和 HashSet的区别并不大,HashSet的实现就是依赖于HashMap,利用适配器模式(HashSet里面包含了一个HashMap)。因此搞清楚HashMap基本也就理解了HashSet。

HashMap的底层实现是数组+链表,借助hash表,处理hash冲突使用的是冲突链表方式(另一种解决冲突方法为开放地址法)。

根据上图,还可以看出是否产生冲突和选定的hash函数以及HashMap的大小有关系,在对HashMap进行迭代时,首先要对整个table进行遍历找到相应的bucket然后再对整个冲突链表遍历,因此对需要频繁迭代的场景,不宜将HashMap初始大小设置过大。

HashMap有连个关键参数,初始容量(inital capacity)、负载因子(load factor),初始容量指定了table的大小,这个参数和哈希函数会影响到冲突的频繁性,负载因子用来指定自动扩容的临界值,当entry(存放键值对的对象)的数量超过capacity*load_factor时,会进行自动扩容和重新哈希。
 

WeakHashMap是基于弱引用的HashMap,它里面的entry随时可能呗GC,因此对WeakHashMap的调用结果是不确定的,WeakHashMap的使用主要集中在缓存场景

弱引用区别于强引用,如果一个对象具有弱引用,在GC线程扫描内存区域的过程中,不管当前内存空间足够与否,都会回收内存。如利用jdk中的ThreadLocal就是弱引用的。
 

HashMap不是线程安全的,HashTable是线程安全的,但是其安全性由全局锁保证,因此效率很低。而ConcurrentHashMap 是将锁的范围细化来实现高效并发的。 基本策略是将数据结构分为一个一个 Segment(每一个都是一个并发可读的 hash table, 即分段锁)作为一个并发单元。 为了减少开销, 除了一处 Segment 是在构造器初始化的, 其他都延迟初始化。 并使用 volatile 关键字来保证 Segment 延迟初始化的可见性问题。 
jdk1.8对ConcurrentHashMap做了一些改进: 
改进一:取消segments字段,直接采用transient volatile HashEntry< K,V>[] table保存数据,采用table数组元素作为锁,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率。 
改进二:将原先table数组+单向链表的数据结构,变更为table数组+单向链表+红黑树的结构。在冲突链表长度过长的情况,如果还是采用单向链表方式,那么查询某个节点的时间复杂度为O(n);因此,对于个数超过8(默认值)的列表,jdk1.8中采用了红黑树的结构,那么查询的时间复杂度可以降低到O(logN),可以改进性能。
 

 

 

 

 

(ps:jdk1.8对hashmap进行了很多优化,当冲突链表长度大于8时使用红黑树解决冲突,从而在链表过长是提高查找效率)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值