java学习日记-集合-set-map

23 篇文章 0 订阅

回顾:

集合

Collection 定义集合能干什么

int size();
boolean empty();
boolean add(E e);
boolean remove(E e);

List:有序可重复的集合

有序:有下标(每个添加的元素都有一个序号,序号从0开始,每次自增1)

int size();
boolean empty();
boolean add(E e);
boolean remove(E e);

E get(int index);
int indexOf(Object obj);
  • ArrayList
    • 底层:数组
    • 默认长度:10
    • 数组什么扩容:长度不够的时候扩容
    • 扩容到多少:原来的1.5倍
  • LinkedList
  • Vecto

Set:无序不可重复

一. List

List是有序可重复的集合

  • 有序:有序号
  • 可重复:值可以相等

1. ArrayList

  • 底层:数组
  • 默认长度:10
  • 数组什么扩容:长度不够的时候扩容
  • 扩容到多少:原来的1.5倍

2. LinkedList

  • 底层:链表 (双向链表)
  • 链表上的节点就是一个Node对象
    • 上个节点
    • 下一个节点
public class LinkedList<E> extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
	//元素个数
	transient int size = 0;
    
    //第一元素  为查询获取元素值
    transient Node<E> first;
    
    //最后一个元素   为方便添加元素
    transient Node<E> last;
    
    public LinkedList() {
    }
    
    //添加元素
    public boolean add(E e) {
        linkLast(e);
        return true;
    }
    
    //添加最后一个元素
    void linkLast(E e) {
        //获取最后一个节点
        final Node<E> l = last;
        /*
         *创建一个节点
           1. 把最后一个节点设置为当前节点的上一个节点
           2. 把要添加的值赋值当前节点的item属性
           3. 把后面一个节点设置为null
         */
        final Node<E> newNode = new Node<>(l, e, null);
        //把当前节点设置为最后一个节点
        last = newNode;
        
        //判断是否是第一次添加
        if (l == null) {
            first = newNode;  // 如果是第一次添加,当前节点也是第一个节点
        } else {
             l.next = newNode; //把原来的最后一个节点的下一个节点设置为新添加的节点
        }
        
        size++;   //元素个数添加1
        modCount++;
    }
    
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    
   Node<E> node(int 2) {
        // assert isElementIndex(index);

        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < 2; i++) {
                x = x.next;
            }
            return x;
        } else {
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    
    
   /*
   	*私有静态内部类
     1. private : 该类只能被所在类使用
     2. static : 可以定义静态属性
   	*/
    
   //Node 就是我们链表上一个的节点(环)     链表就是由一个一个Node对象组成
   private static class Node<E> {
       //该节点(环)上保存的值
        E item;   
       //下一个节点(环)
        Node<E> next;
       //上一个节点(环)
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
    

}

3. Vector

Vector的底层和ArrayList一样都是数组

  • 默认长度也为10
  • 所有的方法都加了synchronized关键字(锁)
    • 表示所有的方法都是线程安全的

4. 比较ArrayList,LinkedList,Vector

  • ArrayList底层是数组,查询速度快,插入和删除的速度的慢

  • LinkedList底层是链表,插入和删除的速度的快,查询慢点

  • Vector底层是数组,它是线程安全的,查询速度快,但是效率相对ArrayList还是差一点

二. Set

Set是无序不可重复的集合

  • 是Collection的子接口

  • 无序 : 没有序号

  • 不可重复 : 可以通过重写equals和hashCode方法定义两个值的比较逻辑

public interface Set<E> extends Collection<E> { 
    
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    
    boolean add(E e);
    boolean remove(Object o);
    void clear();
    int hashCode();
}
package com.qf;

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

public class Demo02 {

	public static void main(String[] args) {
		Set<String> set01 = new HashSet<>();
		set01.add("jackma");
		set01.add("pony");
		set01.add("tomlei");
		set01.add("jackma");
		int size = set01.size();
		System.out.println(size);
		
	}
}

1. HashSet

HashSet是Set的一个实现类

  • 底层就是HashMap
  • 保存值就是保存在一个键值对中的键
public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    //HashSet的底层就是一个HashMap
	private transient HashMap<E,Object> map;
    
    //HashSet构造方法
    public HashSet() {
        //创建一个HashSet就是创建一个HashMap对象
        map = new HashMap<>();
    }
    
    /*
     * 当向HashSet中添加元素时,就是往map添加一个键值对,把值赋值给键
     * HashMap的键是不可以重复的,且判断重复的规则和HashSet一致
     */
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

}
package com.qf;

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

public class Demo02 {

	public static void main(String[] args) {
		
		//创建保存学生的集合
		Set<Student> set02 = new HashSet<Student>();
		
		Student stu01 = new Student();
		stu01.setName("jackma");
		stu01.setCardId("666");
		
		Student stu02 = new Student();
		stu02.setName("pony");
		stu02.setCardId("777");
		
		Student stu03 = new Student();
		stu03.setName("jackma2");
		stu03.setCardId("666");
		
		/*
		 * 如果想让两个对象重复
		 * 一定要重写equals和hashCode方法
		 * 
		 * 如果添加了重复的数据,不是后面的覆盖前面的,而是后面的没有添加成功
		 */
		System.out.println(stu03.equals(stu01));
	//	System.out.println(stu03==stu01);
		
		set02.add(stu01);
		set02.add(stu02);
		set02.add(stu03);
		
		System.out.println(set02.size());
		
		for(Student stu : set02) {
			System.out.println(stu.toString());  //com.qf.Student@d576
		}
		
		
		/*
		 * 定义电脑
		 * 	品牌 价格 编号
		 * 使用set集合保存多个电脑
		 * 如果两个电脑的编号相等,那么就是重复的数据
		 */
		
	}
}

//表示学生类
class Student {
	private String name;   //姓名
	private String cardId; //身份证号码
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getCardId() {
		return cardId;
	}
	public void setCardId(String cardId) {
		this.cardId = cardId;
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((cardId == null) ? 0 : cardId.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 (cardId == null) {
			if (other.cardId != null)
				return false;
		} else if (!cardId.equals(other.cardId))
			return false;
		return true;
	}
	
	@Override
	public String toString() {
		return "Student [name=" + name + ", cardId=" + cardId + "]";
	}
	
}

2. LinkedHashSet

LinkedHashSet是Set的一个实现类,会记录下保存数据的顺序

package com.qf;

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

public class Demo02_2 {

	public static void main(String[] args) {
		/*
		 * Set集合
		 * 1. HashSet		 不会记录顺序,但是顺序一旦确定了,也是不会改变
		 * 2. LinkedHashSet  记录下元素添加的顺序
		 */
	//	Set<Student> set = new HashSet<Student>();
		Set<Student> set = new LinkedHashSet<Student>();

		set.add(new Student("tomlei","777"));
		set.add(new Student("pony","888"));
		set.add(new Student("jackma","666"));
		
		for(Student stu : set) {
			System.out.println(stu.toString());  
		}
		
	}
}

3. TreeSet

TreeSet会自动为元素进行排序

package com.qf;

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

public class Demo02_3 {

	public static void main(String[] args) {
		Set<Dog> set = new TreeSet<Dog>();
		
		set.add(new Dog("阿黄", 3));
		
		set.add(new Dog("旺财", 5));  // 3-4-5 
		set.add(new Dog("小强", 4));
		set.add(new Dog("大强", 4));
		
		for(Dog d : set) {
			System.out.println(d);
		}
		
		System.out.println("-----------------------------------------");
		
		/*
		 * 定义一个考生类
		 * 	姓名,性别,成绩
		 * 第一个集合保存多个考生的信息(对象),按照成绩倒叙打印考生信息
		 */
		
		Set<Examinee> set02 = new TreeSet<Examinee>();
		set02.add(new Examinee("jackma", "男", 100));//   100 99 98
		set02.add(new Examinee("pony", "男", 100));   //98-99=-1
		set02.add(new Examinee("tomlei", "男", 100));
		
		/*
		 *  Examinee [name=pony, gedner=男, score=98]
			Examinee [name=tomlei, gedner=男, score=99]
			Examinee [name=jackma, gedner=男, score=100]

		 */
		for(Examinee e : set02) {
			System.out.println(e);
		}
		
	}
}

class Examinee implements Comparable<Examinee>{
	private String name;
	private String gedner;
	private Integer score;
	
	public Examinee() {
		
	}
	
	public Examinee(String name, String gedner, Integer score) {
		super();
		this.name = name;
		this.gedner = gedner;
		this.score = score;
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getGedner() {
		return gedner;
	}
	public void setGedner(String gedner) {
		this.gedner = gedner;
	}
	public Integer getScore() {
		return score;
	}
	public void setScore(Integer score) {
		this.score = score;
	}
	
	@Override
	public int compareTo(Examinee o) {
		return o.getScore() - this.score;
	}

	@Override
	public String toString() {
		return "Examinee [name=" + name + ", gedner=" + gedner + ", score=" + score + "]";
	}
	
	
}

/*
 * Dog类实现Comparable接口
 * 	表示狗和狗对象之间是可以比较的
 * 		在compareTo方法中定义定义比较的规则
 * 		- 0 : 表示两个对象相等
 *      - 正数: 前面的大于后面的 
 *      - 负数: 前面的小于后面的
 * 
 */
class Dog implements Comparable<Dog>{
	String name;
	Integer code;
	
	public Dog() {
	}
	
	public Dog(String name, Integer code) {
		super();
		this.name = name;
		this.code = code;
	}

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

	@Override
	public int compareTo(Dog o) {
		
		/*- 0 : 表示两个对象相等
		 *- 正数: 前面的大于后面的 
		 *- 负数: 前面的小于后面的	
		 */
		return this.code - o.code;
	}

	
}

三. Map

Map也是一个集合

在java中用的最多就三种集合 List,Set,Map

- List,Set是Collection的子集合
- Map是独立的集合

Map是一种键值对的集合

  • Key
  • Value
public interface Map<K,V> {
    
    int size();
    boolean isEmpty();
    
    boolean containsKey(Object key);
    boolean containsValue(Object value);
    
    //保存数据  value表示保存的值, key表示保存的值的名字
    V put(K key, V value);
    //通过key值获取value值
    V get(Object key);
    //根据key值删除键值对
    V remove(Object key);
    
    void putAll(Map<? extends K, ? extends V> m);
    void clear();
    
    //获取Map所有的Key值,key是不能重复的
    Set<K> keySet();
    
    //获取Map所有的Value值,Value是可以重复的
    Collection<V> values();
    
    Set<Map.Entry<K, V>> entrySet();
}

//键值对接口   我们保存在Map中的每一个键值对(k-v)
interface Entry<K,V> { 
	
	K getKey();
    
    V getValue();
}
package com.qf;

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

public class Demo03 {

	public static void main(String[] args) {
		/*
		 * String : 键的泛型,表示取的名字的数据类型
		 * String  : 值的泛型,表示存的值的数据类型
		 */
		Map<String, String> map = new HashMap<String, String>();
		
		//put添加数据,为每个值,定义一个名字
		map.put("jackma", "小云");
		map.put("pony", "小腾");
		map.put("tomlei", "小军");
		
		//根据起的名字,获取值
		String v1 = map.get("jackma");
		System.out.println(v1);
		
		
		/*
		 * 定义一个保存Student对象的Map集合,名字(key)为该学生对象的姓名
		 * 
		 * 保存三个对象
		 * 
		 * 根据名字获取对象值
		 */
		
	}
}
package com.qf;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Demo03_2 {

	public static void main(String[] args) {
		/*
		 * 定义一个集合,保存班级学生的姓名,以及其成绩
		 */
		Map<String, Double> map = new HashMap<String, Double>();
		
		//添加数据
		map.put("jackma", 99.8);
		map.put("pony", 100.0);
		map.put("tomlei", 99.8);
		
		/*
		 * 1. 在map中key值是不能重复的
		 * 	1.1 和HashSet中判断值是否相等,是一致的
		 * 		- hashCode相等,
		 *      - equals为true
		 *  1.2 当key值重复时,后面的值会覆盖前面的值
		 * 2. value值是可以相等
		 * 3. key,value值都可以为null
		 */
		map.put("tomlei", 99.0);
		map.put(null, null);
		
		//根据key值删除键值对
		map.remove(null);
		
		//获取map中有多少个键值对
		int size = map.size();
		System.out.println(size);
		
		//通过key获取value值
		double score = map.get("tomlei");
		System.out.println(score);
		
		//获取map中所有的键值
		Set<String> keySet = map.keySet();
		
		//遍历key,在通过key获取所有的value
		for(String key : keySet) {
			System.out.println(key+":"+map.get(key));
		}
		
		System.out.println("----------------------------------");
		/*
		 * 获取map中所有键值对 对象
		 * Entry<String, Double> : 每一个键值对都是一个Entry的对象
		 */
		Set<Entry<String, Double>> entrySet = map.entrySet();
		
		for(Entry<String, Double> e : entrySet) {
			String key = e.getKey();
			Double value = e.getValue();
			System.out.println(key + ":"+ value);
		}
		
		/*
		 * 创建一个Map对象
		 * 1. Key值为考生
		 * 2. Value值为成绩
		 * 3. 如果两个考生姓名相同,那么他们就相等
		 * 
		 * 测试
		 */
	}
}

1. HashMap

HashMap是Map的一个实现类

  • 底层:数组加链表(Node,单向链表)
    • Node中有五个属性 hash,key,value,next
  • 数目默认长度为16
  • 数组的扩容时机:当容量达到扩容因子定义的个数时,扩容
    • 默认扩容因子0.75 16*0.75=12 数组中位置有12个已经被占用,就被扩容
  • 数组扩容后的大小:扩容为原来的两倍
    • 16 12
    • 32 24
    • 64 48
  • Node对象的保存
    • 当我们添加一个键值对时,会创建一个Node对象
      • key
      • value
      • hash
    • 首先通过key获取他的hash值
      • hash值能够确定该对象保存在数组中位置
      • 如果该位置没有值,就把该对象直接放在该位置
      • 如果该位置有值,那么就该对象放在原对象后面(next属性)
        • 数组中的一个元素存放节点的个数小于8时,存放的形式是二叉树
        • 数组中的一个元素存放节点的个数达到8个时,二叉树转化为红黑树
public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable { 
    
    //默认容量    0001  0000
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // 16
    
    //最大容量 2^30
    static final int MAXIMUM_CAPACITY = 1 << 30;
    
    //加载因子,定义扩容的时机
    static final float DEFAULT_LOAD_FACTOR = 0.75f;
    
    //树化阈值(临界值)
    static final int TREEIFY_THRESHOLD = 8;
    
    //反向 树化阈值
    static final int UNTREEIFY_THRESHOLD = 6;
    
    //最小树化容量
    static final int MIN_TREEIFY_CAPACITY = 64;
    
    //保存数据的Node数组,我们保存的每一个键值对会保存在一个Node对象中,
    //Node对象又会保存在一个数组中
    //HashMap的底层是 数组+链表
    transient Node<K,V>[] table;
    
    //个数
    transient int size;
    
    //
    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
    
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    
    /*
    	hash: key的hash值
    	key: key值
    	value:值
    */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab;  //node数组
        Node<K,V> p; 	  //node对象
        int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    //设置数组的容量
     final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        @SuppressWarnings({"rawtypes","unchecked"})
            Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }
    
    
    
}

//HashMap中保存数据的节点
static class Node<K,V> implements Map.Entry<K,V> {
    	//key的hash值
        final int hash;
    	//k值
        final K key;
    	//value值
        V value;
    	//下一个节点
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值