集合(容器)

本文深入讲解Java集合框架,包括泛型、List、Set、Map等核心接口与其实现类,如ArrayList、LinkedList、HashMap、TreeMap的特性及使用场景,同时探讨了容器操作、迭代器遍历以及自定义容器实现。

基于尚学堂视频课程的学习

 

目录

泛型:

Collection接口中的方法

多个容器的操作

List 

手工方法实现ArrayList

LinkedList

Vector

Map接口

HashMap

TreeMap

Set

Irerator迭代器遍历容器元素(LIst,map,set)

Connections工具类的使用



数组也是一种容器,可以用来放对象或是基本数据类型

  • 优点:
    • 快速访问数组,效率高
    • 简单的线性序列
  • 缺点:
    • 不灵活,数组需要实现定义好容量

 

泛型:

泛型是JDK1.5后加入的,它是帮助我们建立类型安全的集合;

泛型的本质是“ 数据类型的参数化 ”,可以把泛型理解为数据类型的一个占位符(形式参数),告诉编译器,在传入的时候一定要传入实际类型。

public class test3 {
    public static void main(String[] args) {
        CollectionTest<String> ct = new CollectionTest<String>();
        ct.setData("1111",0);
        //如果我们没有用泛型,因为在CollectionTest定义的结合为Object,我们需要
        //String i =(String)ct.getData(0);
        String i = ct.getData(0);
    }
}
//类似于是一个集合类(满足存取的功能)
class CollectionTest<E>{        //字符可以是任何标识符,一般采用<T,V,E>
    Object[] obj = new Object[5];
    public void setData(E o,int index){
        obj[index] = o;
    }

    public E getData(int index){
        return (E)obj[index];
    }
}

 

Collection接口中的方法

 

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionTest0 {
    public static void main(String[] args) {
        Collection<String> ct = new ArrayList<String>();
        System.out.println(ct.size());
        System.out.println(ct.isEmpty());

        ct.add("田坤");
        ct.add("田大坤");
        ct.add("田小坤");
        System.out.println(ct.size());
        System.out.println(ct);

        Object[] a= ct.toArray();
        System.out.println(Arrays.toString(a));

        ct.remove("田大坤");
        System.out.println(ct);

        System.out.println(ct.contains("田坤"));
        ct.clear();
        System.out.println(ct.isEmpty());

        //这里面有的方法没有写到,后面再说。
        //因为后面的讲的LIST SET 都是Collection的子类,所以,这些以上方法都是类似的,后不在重复
    }
}
0
true
3
[田坤, 田大坤, 田小坤]
[田坤, 田大坤, 田小坤]
[田坤, 田小坤]
true
true

多个容器的操作

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

public class CollectionTest0 {
    public static void main(String[] args) {
        Collection<Integer> ct1 = new ArrayList<Integer>();
        ct1.add(1);
        ct1.add(2);
        ct1.add(3);
        Collection<Integer> ct2 = new ArrayList<Integer>();
        ct2.add(1);
        ct2.add(2);
        ct2.add(6);

//      ct1.addAll(ct2);
        System.out.println(ct1);
        //在ct1中移除所以含有ct2中的数据      
//      ct1.removeAll(ct2);
        System.out.println(ct1);

        ct1.retainAll(ct2);
        System.out.println(ct1);

    }
}

List 

有序,可重复的容器

                 有序:LIst中每个元素都有索引标记,可以根据元素的索引标记(List中的位置)访问元素,从而精确的控制这些元素

                 可重复:List加入重复的元素,   满足    e1.equals(e2) 的元素重复加入容器

List接口的三个实现类:

  1. ArrayList(数组)
  2. LinkedList(链表)
  3. Vector

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

public class CollectionTest0 {
    public static void main(String[] args) {
        test1();
    }
    public static void test1(){
        List<String> list1 = new ArrayList<String>();
        list1.add("A");
        list1.add("B");
        list1.add("C");

        list1.add(2,"田坤");
        System.out.println(list1);

        list1.remove(3);
        System.out.println(list1);

        list1.set(1,"E");
        System.out.println(list1);

        list1.add("C");
        list1.add("E");
        System.out.println(list1);
        System.out.println(list1.indexOf("E"));
        System.out.println(list1.lastIndexOf("E"));
    }
}
[A, B, 田坤, C]
[A, B, 田坤]
[A, E, 田坤]
[A, E, 田坤, C, E]
1
4

 

手工方法实现ArrayList

package com.tk.collection;

/**
 * 手工实现Arraylist的简单原理   get add set toString remove size
 * @data :2019-5-11上午10:14:42
 * @author :田坤
 */
public class List02<E> {
	
	//定义一个数组
	Object[] arr = null;
	//数据的默认大小
	private static final int ARRAY_OPACITY = 10;
	
	//容器的大小
	private int size = 0;
	
	public List02() {
		arr = new Object[ARRAY_OPACITY];
	}
	
	public List02(int count){
		if(count <0)
			throw new RuntimeException("数组个数不能小于0");
		else if(count == 0)
			arr = new Object[ARRAY_OPACITY];
		else arr = new Object[count];	
	}
	
	//取
	public E get(int index){
		return (E)arr[index];
	}
	
	//放
	public void add(E obj){
		//扩容
		if(size == arr.length){
			Object[] newarr = new Object[size+(size>>1)];  //size + size/2
			System.arraycopy(arr, 0, newarr, 0, size);
			arr = newarr;
		}
		arr[size] = (E)obj;
		size++;
	}
	
	//移除某个元素
	public void remove(int index){
		if(index <0 || index >=size)
			throw new RuntimeException("索引越界");
		System.arraycopy(arr, index+1, arr, index,size-index-1);
		arr[size] = null;
		size --;
	}
	
	//修改某个索引的数据
	public void set(int index,E obj){
		if(index<0 || index >=size)
			throw new RuntimeException("索引越界");
		arr[index] = obj;
	}
	
	//数组的大小
	public int size(){
		return size;
	}
	
	//
	public String toString(){
		StringBuilder sb = new StringBuilder();
		sb.append("[");
		for (int i = 0; i < arr.length; i++) {
			if(arr[i]!= null)
				sb.append(""+arr[i]+",");
		}
		sb.setCharAt(sb.length()-1, ']');
		return sb.toString();
	}
	
	public static void main(String args[]) {

		List02<String> list = new List02<>(0);
		for(int i = 0; i < 20; i++){
			list.add("田坤"+i);
		}
		System.out.println(list.get(2));
		list.set(2, "宋旭东");
		list.remove(-1);
		System.out.println(list.toString());
	}
}

 

 

LinkedList

linkedList底层使用双向链表实现的存储。

特点:查询效率低,增删效率高,线程不安全

双向链表:每个数据节点都有俩个指针,分别指向前一个和后一个节点

每个节点中的内容

public class Node {

	public Node previous;	//前指针
	public Object value;	//节点数据
	public Node next;		//后指针

 

package com.tk.collection;

/**
 * 自定义一个链表
 * @data :2019-5-11上午11:28:30
 * @author :田坤
 */
public class LinkedList01<E>{
	private Node first;
	private Node last;
	
	private int size = 0;
	
	
	
	public E get(int index){
		if(index < 0 || index >= size)
			throw new RuntimeException("索引越界");
		Node node = new Node();
		node = first;
		for(int i = 0; i < index;i++ ){
			node = node.next;
		}
		return (E) node.value;
	}
	
	public Node getNode(int index){
		if(index < 0 || index >= size)
			throw new RuntimeException("索引越界");
		Node node = new Node();
		node = first;
		for(int i = 0; i < index;i++ ){
			node = node.next;
		}
		return node;
	}
	
	//添加数据
	public void add(E obj){
		Node node = new Node(obj);
		
		if(first == null){
			first = node;
			last = node;
			size ++;
		}else{
			node.previous = last;
			node.next = null;
			
			last.next = node;
			last = node;
			size ++;
		}
	}
	
	public int size(){
		return size;
	}
	
	
	public void ToString() {
		Node node = new Node();
		node = first;
		while(node!=null){
			System.out.println(node.value);
			node = node.next;
		}
	}
	
	
	public void remove(int index){
		if(index < 0 || index >= size)
			throw new RuntimeException("索引越界");
		
		Node node = getNode(index);
		
		node.next.previous = node.previous;
		node.previous.next = node.next;
		size --;
	}
	
	public void insert(int index,E value){
		if(index < 0 || index >= size)
			throw new RuntimeException("索引越界");
		
		Node node = new Node(value);
		Node temp = getNode(index);
		
		node.previous = temp.previous;
		node.next = temp;
		temp.previous.next = node;
		temp.previous = node;
		
		size ++;
		
	}

	
	public static void main(String args[]) {
		LinkedList01<String> ll = new LinkedList01();
		ll.add("11");
		ll.add("22");
		ll.add("33");
		ll.ToString();
		System.out.println(ll.get(2));
		System.out.println(ll.size());
		ll.remove(1);
		ll.insert(1, "4444");
		ll.ToString();
	}
}

 

Vector

底层启用数组实现了List接口,相关方法加入了同步检测,所以“效率低,安全性高

        Vector中的   get方法

 

如何选择AarryLis、LiinkedLst、Vector

  1. 需要线程安全时,使用vector
  2. 不存在多线程安全问题,并且查找比较多用ArrayList
  3. 不存在多线程安全问题,并且增加和删除元素多用LinkedList

 

 

 

Map接口

 

 

package com.tk.collection;

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

public class Map01 {
	public static void main(String args[]) {
		Map<Integer, String> m1 = new HashMap<Integer, String>();
		m1.put(1, "111");
		m1.put(2, "222");
		m1.put(3, "333");
		System.out.println(m1.toString());
		System.out.println(m1.get(1));
		System.out.println(m1.size());
		System.out.println(m1.isEmpty());
		System.out.println(m1.containsKey(1));
		System.out.println(m1.containsValue(222));
		
		Map<Integer, String> m2 = new HashMap<Integer, String>();
		m2.put(4, "444");
		m2.put(5, "555");
		
		m1.putAll(m2);
		System.out.println(m1);
		
		//map键不能重复,如果有重复的(根据equals来判断),则新的覆盖旧的
		m1.put(2, "666");
		System.out.println(m1);
		
	}
}

 

 

HashMap

HashMap底层实现使用了Hash表,这是一种非常重要的数据结构,对于我们很多的技术都有帮助(如:redis的核心技术和HashMap一样)。

数据结构中由数组和链表来实现对数据的存储:

数组:占用中间连续,寻址容易,查询速度快,但是增加和删除效率低

链表:占用空间不连续,寻址困难,查询速度慢,但是,增加和删除效率非常高

 

哈希表的本质就是= 数组 + 链表

 

HashMap源码中数据的初始化

Entry[ ]  table是HashMap的核心数组结构,我们称之为“位桶结构”,Entry是HashMap中的静态内部类。数据的初始化

Entry中的对象:

  • key :键对象
  • value   : 值对象
  • next    :下一个节点
  • hash   :键对象的Hash值

每一个Entry都是一个单向链表结构

Entry []  的数组结构为:(HashMap的结构)

 

1、存储数据的过程:put(key ,  value)

 

我们的目的是将"ket  value"俩个对象存储到Entry数组中去

1、获取key对象的hashcode()

           hashcode()方法

2、根据hashcode方法计算出hash值(要求在【0-数组长度-1】区间中)

      (1)、计算算法

           hash = hashcode / hashcode

           键值对对象都会存储到索引值为1的位置,每存储一个对象,就会发生  hash冲突  hashMap会 退化成一个链表

     (2)、简单和常用的算法(相除取余算法)

          hash = hashcode / 数组长度

         但是除法效率低下,JDK在后来改进了算法,首先约定数组的长度必须是2的整数幂,然后采用位运算进行取余

          hash值 =  hashcode & (数组长度-1)

为了更好的散列效果,JDK对hashcode进行了俩次散列处理(核心目的是为了分布的更加均匀)

(3)、生成Entry对象

(4)、将Entry对象放到table数组中

       如果数组中没有值,直接将entry对象放到数组中去,并且entry对象的next值为 null ,如果数组对应位置有数据,将其放在next后

 

JDK8时,如果链表长度大于8时,链表就换成了红黑树,大大提高了查询的效率

 

2、获取数据的过程:get(key )

           先需要获取键值对对象,进而在返回value值

(1)、获取键对象的hashcode,获取到hash值。定位到数组的位置

(2)、在链表上个挨个比较key对象(equals比较)

(3)、返回equals为true的节点对象

 

JAVA规定俩个内容相同(equals为true)的对象必须有相同的hashcode.

 

手动实现一个HashMap

1、HashMap的节点类

/**
 * HashCode的节点类
 * @data :2019-5-11下午4:44:16
 * @author :田坤
 */
public class Node0 {
	public int hash;
	public Object key;
	public Object value;
	public Node0 next;

2、HashMap的实现

 

package com.tk.collection;

/**
 * 自定义实现HashMap
 * @data :2019-5-11下午4:43:42
 * @author :田坤
 */
public class MyMap<K,V> {
	
	Node0[] table;  //位桶数组
	int size = 0;   //节点的个数
	private static final int HASHMAP_OPACITY = 16;   //HashMap数组的默认大小   2的整数幂
	
	public MyMap(){
		table = new Node0[HASHMAP_OPACITY];
	}
 	
	
	public void put(K key,V value){
		Node0 node = new Node0();
		node.hash = myHash(key.hashCode(), HASHMAP_OPACITY);
		node.value = value;
		node.key = key;
		node.next = null;
		
		Node0 lastNode = null;  //在遍历的最后一个节点
		
		//根据此节点的hash值获取到hash值相对应的链表中
		Node0 temp = new Node0();
		temp = table[node.hash];
		//如果索引为hash索引的下标没有数据
		if(temp == null){
			//节点为空直接将节点放进去 
			table[node.hash] = node;
		}else{
			//数组元素不为空,遍历
			while(temp != null){
				if(temp.key.equals(key)){
					temp.value = value;
				}
				else{
					lastNode = temp;
					temp = temp.next;
				}
				
			}
			
			lastNode.next = node;
		}
	}
	
	
	private int myHash(int hashCode,int length){
		System.out.println("hash in myHash"+(hashCode%(length-1)));  //取模运算效率低
		System.out.println("hash in myHash"+(hashCode&(length-1)));  //直接位运算,效率高
		
		return hashCode&(length-1);
	}
	
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("{");
		for (int i = 0; i < table.length; i++) {
			Node0 temp = table[i];
			while(temp != null){
				sb.append(""+temp.key+":"+temp.value+",");
				temp = temp.next;
			}
		}
		sb.setCharAt(sb.length()-1, '}');
		return sb.toString();
	}
	
	public K get(K key){
		int hash = myHash(key.hashCode(), HASHMAP_OPACITY);
		Node0 temp = table[hash];
		while(temp != null){
			if(temp.key.equals(key))
				break;
			temp = temp.next;
		}
		
		return (K)temp.value;
	}
	

	public static void main(String args[]) {
		MyMap<Integer,String> mm = new MyMap<>();
		mm.put(1, "111");
		mm.put(2, "222");
		mm.put(3, "333");
		mm.put(16, "333");
		mm.put(17, "333");	
		
		System.out.println(mm.toString());
		System.out.println(mm.get(3));
	}
}

 

HashMap和HashTable的区别

HashMap:线程不安全,效率高 。允许key和value为null

HashTable:线程安全,效率低。 允许key和value为null

 

 

 

TreeMap

TreeMap是红黑二叉树的经典实现

 

 

package com.tk.collection;

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

public class TreeMapTest {
	public static void main(String args[]) {
		Map<Integer,String> map = new TreeMap<>();
		map.put(1, "11");
		map.put(3, "33");
		map.put(6, "66");
		map.put(4, "44");
		
		System.out.println(map.toString());
		
		Map<Student, String> stumap = new HashMap<Student, String>();
		stumap.put(new Student(1, 88), "张三");
		stumap.put(new Student(3, 99), "李四");
		stumap.put(new Student(5, 88), "王五");
		stumap.put(new Student(15, 55), "赵六");
		
		for (Student stu : stumap.keySet()) {
			System.out.println(stu);
		}
	}
}

class Student implements Comparable<Student>{
	int id;
	int grade;
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public int getGrade() {
		return grade;
	}
	public void setGrade(int grade) {
		this.grade = grade;
	}
	@Override
	public String toString() {
		return "Student [id=" + id + ", grade=" + grade + "]";
	}
	public Student(int id, int grade) {
		super();
		this.id = id;
		this.grade = grade;
	}
	public Student() {
		super();
	}
	@Override
	public int compareTo(Student o) {   //负数: 小于          0 :等于             正数:大于
 		if(this.grade>o.grade)
			return 1;
		else if(this.grade<o.grade)
			return -1;
		else if(this.id > o.id)
			return 1;
		else if(this.id < o.id)
			return -1;
		return 0;
	}
	
	
	
	
	
	
}

Tree

 

Set

HashSet源码

hashset是基于hashmap实现的

 

Irerator迭代器遍历容器元素(LIst,map,set)

package com.tk.collection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class iteratorTest {

	public static void main(String args[]) {
			map1();
			list1();
			set();
	}
	
	public static void map1(){
		Map<Integer,String> map = new HashMap<>();
		map.put(1, "111");
		map.put(2, "222");
		map.put(3, "333");
		
		Set<Entry<Integer, String>> entrySet = map.entrySet();
		Iterator<Entry<Integer, String>> iterator = entrySet.iterator();
		while(iterator.hasNext()){
			Entry<Integer, String> next = iterator.next();
			System.out.println(next.getKey()+"-------"+next.getValue());
		}
		
		Set<Integer> keySet = map.keySet();
		Iterator<Integer> iterator2 = keySet.iterator();
		while(iterator2.hasNext()){
			int key = iterator2.next();
			System.out.println(key+"-------"+map.get(key));
		}
	}
	
	
	public static void list1(){
		List<String> ls = new ArrayList<String>();
		ls.add("11");
		ls.add("22");
		ls.add("33");
		ls.add("44");

		Iterator<String> iterator = ls.iterator();
		
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}
	
	public static void set(){
		Set<String> set= new HashSet<>();
		set.add("11");
		set.add("22");
		set.add("33");

		Iterator<String> iterator = set.iterator();
		
		while(iterator.hasNext()){
			System.out.println(iterator.next());
		}
	}
	
}

 

 

Connections工具类的使用

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值