java集合框架总结

集合


集合就是容器

容器有:数组 StringBuffer StringBuilder

  • 数组:
  1. 只能够存储一组相同数据类型的容器
  2. 数组中只有length属性,没有供我们操作对象的方法
  • StringBufferStringBuilder
  1. 只能够存储字符串类型
  2. 有对字符串进行增加删除修改的方法

集合的功能:

  1. 作为容器存在
  2. 集合既能够存储引用数据类型,但是集合可以存储基本数据类型对应的包装类类型,强制我们使用包装类
  3. 集合同时也可以对集合中的元素进行随意增加删除修改查询
  4. 集合是可以动态扩容,缩容

数组和集合的区别:

长度

数组的长度固定不变的

内容

数组存储的是同一种数据类型的元素

集合可以存储不同类型的元素

数据类型

数组可以存储基本数据类型,也可以存储引用数据类型

集合只能存储引用数据类型

集合的概述:集合是一套容器框架

为什么集合是一套框架?

​ 容器中的元素可能是有序的,无序,可排序,重复的,唯一的,线程安全的,线程不安全等等特点.

集合框架体系图
在这里插入图片描述

既然是框架,肯定有框架体系图,顶层父类设计为接口还是设计成类?

集合顶层父类应该设计为接口,这个接口叫做Collection【单列集合】和Map【双列集合】

Collection

增加

boolean add(Object e)

boolean addAll(Collection c)

删除

void clear()
    
boolean remove(Object o)
    
boolean removeAll(Collection o)

修改

查询(遍历)

Object[]  toArray()

<T> T[] toArray(T[] a)

Iterator<E> iterator()

interface Collection extends Iterable{
Iterator<T> iterator();

}

interface Iterable{

Iterator<T> iterator();

}

public interface Iterator<E>{

boolean hasNext();

Object next();

}

判断

boolean contains(Object o)

boolean containAll(Collection c)

boolean isEmpty()

获取

int size()

其它

boolean retainAll(Collection<?> c) 求两个集合的交集,返回原集合是否发生改变,发生了改变返回true,否则返回false

并发修改异常

ConcurrentModificationException

异常名称:并发修改异常

产生原因:在使用迭代器迭代集合的同时,使用元集合修改元素

解决办法:

  1. 不使用迭代器遍历,使用普通for遍历
  2. 使用迭代器,使用列表迭代器
  3. toArray

foreach是集合和数组的专属迭代器

学习源码遍历集合的方式:

public String toString(){

Iterator<E> it = c.iterator();

if(!it.hasNext())

return "[]";

StringBuilder sb = new StringBuilder();

sb.append("[");

for(;;){

E e = it.next();

sb.append(e == this?"(this Collection)" : e);

if(!it.hasNext())

return sb.append(']').toString;

sb.append(',').append(' ');

}

}

List

概述

有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。

Collection将集合划分为两大类:

  1. List集合
  2. Set集合

List接口的特点

  1. 有序【存储有序】

  2. 可重复

  3. 可以存储 null

  4. 部分子集合线程安全,部分不安全 例如 ArrayListVector

  5. 有索引,针对每个元素能够方便地查询和修改

  6. 判断元素是否重复依赖于equals方法

    ​ a. 如果元素是系统类,不需要重写equals方法

    ​ b. 如果是自定义类,就需要我们按需求重写 equals方法

List接口的常用方法

增加

void add(int index, E element) 在指定 index 索引处理插入元素 element
boolean addAll(int index, Collection<? extends E> c) 在指定 index 索引处理插入集合元素 c

删除

E remove(int index) 删除指定索引 index 处的元素

修改

E set(int index, E element) 修改指定索引 index 处的元素为 element			

遍历

E get(int index) + int size() for循环遍历集合中的每一个元素
ListIterator<E> listIterator() 通过列表迭代器遍历集合中的每一个元素
ListIterator<E> listIterator(int index) 通过列表迭代器从指定索引处开始正向或者逆向遍历集合中的元素

获取

E get(int index) 获取指定索引处的元素
int indexOf(Object o) 从左往右查找,获取指定元素在集合中的索引,如果元素不存在返回 -1
int lastIndexOf(Object o) 从右往左查找,获取指定元素在集合中的索引,如果元素不存在返回 -1
List<E> subList(int fromIndex, int toIndex) 截取从 fromIndex 开始到 toIndex-1 处的元素

List接口的遍历方式

  1. toArray
  2. Iterator
  3. foreach
  4. 普通for
  5. ListIterator

List接口去除重复元素

​ 方式一:创建一个新的集合去除重复元素再使用地址传递

​ 方式二:在原集合的基础上使用选择排序思想去除重复元素

List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("李四");
list.add("李四");
list.add("王五");

for (int i = 0; i < list.size(); i++) {
    for (int j = i + 1; j < list.size(); j++) {
        if (list.get(i).equals(list.get(j))) {
            list.remove(j);
            j--;
        }
    }
}

并发修改异常的处理

异常名称:并发修改异常 java.util.ConcurrentModificationException

产生原因:在使用迭代器迭代的同时使用原集合对元素做了修改

解决办法:

  1. 使用 toArray 方法

  2. 使用 普通 for 遍历

  3. 使用 ListIterator 遍历集合并且使用 列表迭代器修改元素

ArrayList

概述

List 接口的大小可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现
List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector类,除了此类是不同步的。)

特点

  1. 底层数据结构是数组
  2. 增加和删除的效率低,查询和修改的效率高
  3. 能够存储 null 值
  4. 线程不安全,效率高 可以通过 Collections.synchronizedList();变安全
  5. 有索引,能够方便检索
  6. 元素可重复,我们自己可以通过 选择排序去重复
  7. 不可以排序,但是可以通过 Collections.sort();方法排序

注:ArrayList中常用的方法全部来自于 父类 Collection,List,Object.这里不再做详细叙述。

Vector

概述

Vector 类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector 的大小可以根据需要增大或缩小,以适应创建 Vector 后进行添加或移除项的操作。

特点

  1. 底层数据结构是数组
  2. 有索引,能够方便检索
  3. 增加和删除的效率低,查询和修改的效率高
  4. 线程安全,效率低
  5. 能够存储 null 值
  6. 元素可重复【我们自己可以通过选择排序思想去除重复元素】
  7. 不可以排序,但是可以通过 Collections.sort();方法排序

常用方法

增加

public synchronized void addElement(E obj) 添加元素 obj 到集合中
public synchronized void insertElementAt(E obj, int index) 在指定索引 index 处插入元素 obj

删除

public synchronized void removeElementAt(int index) 移除指定索引 index 处的元素
public synchronized void removeAllElements() 移除所有元素

修改

public synchronized void setElementAt(E obj, int index) 修改指定索引 index 的元素为 obj

遍历

public synchronized E elementAt(int index) + size() for循环遍历集合中的所有元素
public synchronized Enumeration<E> elements() 使用 Enumeration 迭代器遍历集合中的元素

获取

public synchronized E firstElement() 获取集合中的第一个元素
public synchronized E lastElement() 获取集合中的最后一个元素
public synchronized E elementAt(int index) 获取指定索引 index 的元素

相关面试题

ArrayList和Vector的区别?
1Vector的方法都是同步的(Synchronized),是线程安全的(thread-safe),而ArrayList的方法不是,由于线程的同步必然要影响性能,因此,ArrayList的性能比Vector好。 
2) 当Vector或ArrayList中的元素超过它的初始大小时,Vector会将它的容量翻倍,而ArrayList只增加50%的大小,这样,ArrayList就有利于节约内存空间。

Stack

概述

Stack 类表示后进先出(LIFO)的对象堆栈。它通过五个操作对类 Vector 进行了扩展 ,允许将向量视为堆栈。它提供了通常的 pushpop 操作,以及取堆栈顶点的 peek 方法、测试堆栈是否为空的 empty 方法、在堆栈中查找项并确定到堆栈顶距离的 search 方法。

特点

  1. 基于栈结构的集合,先进后出
  2. Stack 类是 Vector类的子类,所以该类也是线程安全的,效率低,建议使用 Deque接口的实现类

常用方法

E push(E item) 将元素压入栈底
E pop() 将元素从栈结构中弹出,并作为此函数的值返回该对象,此方法会影响栈结构的大小
E peek() 查看堆栈顶部的对象,但不从栈中移除它。
boolean empty() 测试栈是否为空。
int search(Object o) 返回对象在栈中的位置,以 1 为基数。

注:如果栈中元素为空,再尝试弹栈,将会抛出 EmptyStackException 异常, 而不是 NoSuchElementException

示例代码如下:

Stack<String> stack = new Stack<>();
// 压栈
stack.push("A");
stack.push("B");
stack.push("C");

while (!stack.isEmpty()) {
    System.out.println("栈顶元素:" + stack.peek());
    // 弹栈
    System.out.println("弹出栈顶元素:" + stack.pop());
}

Queue

概述

在处理元素前用于保存元素的 collection。除了基本的 Collection 操作外,队列还提供其他的插入、提取和检查操作。每个方法都存在两种形式:一种抛出异常(操作失败时),另一种返回一个特殊值(null
false,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。

特点

  1. 该接口是队列接口的根接口,先进先出
  2. 该接口提供队列相关两种形式的方法,一种抛出异常(操作失败时),另一种返回一个特殊值(null
    false,具体取决于操作)。插入操作的后一种形式是用于专门为有容量限制的 Queue 实现设计的;在大多数实现中,插入操作不会失败。

常用方法

抛出异常返回特殊值
插入add(e)offer(e)
移除remove()poll()
检查element()peek()

示例代码:

Queue<String> queue = new ArrayDeque<>();
queue.add("A");
queue.add("B");
queue.add("C");

while (!queue.isEmpty()) {
    System.out.println(queue.remove());
}
// 或者
queue.offer("A");
queue.offer("B");
queue.offer("C");

while (!queue.isEmpty()) {
    System.out.println(queue.poll());
}

Deque

概述

一个线性 collection,支持在两端插入和移除元素。名称 deque 是“double ended queue(双端队列)”的缩写,通常读为“deck”。大多数 Deque 实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。

特点

  1. Deque是一个Queue的子接口,是一个双端队列,支持在两端插入和移除元素
  2. deque支持索引值直接存取。
  3. Deque头部和尾部添加或移除元素都非常快速。但是在中部安插元素或移除元素比较费时。
  4. 插入、删除、获取操作支持两种形式:快速失败和返回nulltrue/false
  5. 不推荐插入null元素,null作为特定返回值表示队列为空

常用方法

第一个元素(头部)最后一个元素(尾部)
抛出异常特殊值抛出异常特殊值
插入addFirst(e)offerFirst(e)addLast(e)offerLast(e)
移除removeFirst()pollFirst()removeLast()pollLast()
检查getFirst()peekFirst()getLast()peekLast()

双向队列操作

插入元素

  • addFirst(): 向队头插入元素,如果元素为null,则发生空指针异常
  • addLast(): 向队尾插入元素,如果为空,则发生空指针异常
  • offerFirst(): 向队头插入元素,如果插入成功返回true,否则返回false
  • offerLast(): 向队尾插入元素,如果插入成功返回true,否则返回false

移除元素

  • removeFirst(): 返回并移除队头元素,如果该元素是null,则发生NoSuchElementException
  • removeLast(): 返回并移除队尾元素,如果该元素是null,则发生NoSuchElementException
  • pollFirst(): 返回并移除队头元素,如果队列无元素,则返回null
  • pollLast(): 返回并移除队尾元素,如果队列无元素,则返回null

获取元素

  • getFirst(): 获取队头元素但不移除,如果队列无元素,则发生NoSuchElementException
  • getLast(): 获取队尾元素但不移除,如果队列无元素,则发生NoSuchElementException
  • peekFirst(): 获取队头元素但不移除,如果队列无元素,则返回null
  • peekLast(): 获取队尾元素但不移除,如果队列无元素,则返回null

栈操作

pop(): 弹出栈中元素,也就是返回并移除队头元素,等价于removeFirst(),如果队列无元素,则发生NoSuchElementException

push(): 向栈中压入元素,也就是向队头增加元素,等价于addFirst(),如果元素为null,则发生NoSuchElementException,如果栈空间受到限制,则发生IllegalStateException

引用场景

  1. 满足FIFO场景时
  2. 满足LIFO场景时,曾经在解析XML按标签时使用过栈这种数据结构,但是却选择Stack类,如果在进行栈选型时,更推荐使用Deque类,应为Stack是线程同步
ArrayDeque

概述

Deque 接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止 null 元素。此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList

特点

  1. ArrayDequeDeque 接口的一种具体实现,是依赖于可变数组来实现的
  2. ArrayDeque 没有容量限制,可根据需求自动进行扩容
  3. ArrayDeque不支持值为 null 的元素。
  4. ArrayDeque 可以作为栈来使用,效率要高于 Stack
  5. ArrayDeque 也可以作为队列来使用,效率相较于基于双向链表的 LinkedList 也要更好一些
LinkedList

概述

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现
List 接口外,LinkedList 类还为在列表的开头及结尾 getremoveinsert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列双端队列

特点

  1. LinkedList 底层数据结构是一个双向链表,元素有序,可重复。
  2. LinkedList 在实现数据的增加和删除效率高,查询和修改效率低,顺序访问会非常高效,而随机访问效率比较低。
  3. LinkedList 实现 List 接口,支持使用索引访问元素。
  4. LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
  5. LinkedList 是线程不安全的,效率高

Set

概述

一个不包含重复元素的 collection。更确切地讲,set 不包含满足 e1.equals(e2) 的元素对
e1e2,并且最多包含一个 null 元素

特点

  1. Set接口是无序的
  2. Set 是继承于Collection的接口。它是一个不允许有重复元素的集合。
  3. Set可以存储null值,但是null不能重复
  4. Set的实现类都是基于Map来实现的(HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的)。

代码示例

Set<String> set = new HashSet<>();
set.add("ab");
set.add("ac");
set.add("ba");
set.add("bc");

System.out.println(set);

运行结果: [ab, bc, ac, ba]
HashSet

概述

此类实现 Set 接口,由哈希表(实际上是一个 HashMap 实例)支持。它不保证 set 的迭代顺序;特别是它不保证该顺序恒久不变。此类允许使用 null 元素。

特点

  1. 底层数据结构是 哈希表,HashSet的本质是一个"没有重复元素"的集合,它是通过HashMap实现的。HashSet中含有一个"HashMap类型的成员变量"map,在HashSet中操作函数,实际上都是通过map实现的。

  2. 哈希表保证唯一 依赖hashcode和equals方法

    ​ 原理:
    ​ 首先判断hashCode是否相同
    ​ 不相同
    ​ 就存储到集合中
    ​ 相同
    ​ 比较equals方法是否相同
    ​ 相同 就不存储
    ​ 不相同就以链表的方式存储到集合中

  3. 哈希表导致元素存储无序主要因为系统通过哈希算法计算出来的索引和对象本身的hashCode本身有关,所以这个整数值是无序的,从而存储到集合中自然就是无序的

底层源码分析示例

HashSet<String> hs = new HashSet<>();
set.add("ab");
set.add("ac");
set.add("ba");
set.add("bc");

class HashSet {

	private transient HashMap<E,Object> map;
	private static final Object PRESENT = new Object(); 
	
	public HashSet() {
        map = new HashMap<>();
    }

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
}

class HashMap {
	public V put(K key, V value) {					
        return putVal(hash(key), key, value, false, true);
    }
    // 哈希算法和存储对象本身有关
    static final int hash(Object key) {
        int h;
        							// 哈希值和对象的hashCode有关
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    				
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; 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;
            // 判断元素是否唯一取决于哈希值和equals方法
            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;
    }
}

class String {
	private int hash; // Default to 0
	// 字符串hashCode方法的重写规则
	public int hashCode() {
        int h = hash;
        if (h == 0 && value.length > 0) {
            char val[] = value;
			// ab
            for (int i = 0; i < value.length; i++) {
                h = 31 * h + val[i];  // 31 * 0 + 97
                h = 31 * 97 + 98
            }
            hash = h;
        }
        return h;
    }
    // 字符串equals方法的重写规则
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
}

HashSet存储自定义对象去除重复元素需要重写 hashCode和equals方法

public class HashSetDemo02 {
	public static void main(String[] args) {
		HashSet<Student> hs = new HashSet<>();
		hs.add(new Student("zhangsan", 18));
		hs.add(new Student("lisi", 20));
		hs.add(new Student("zhangsan", 18));
		hs.add(new Student("wangwu", 30));
		
		for (Student s : hs) {
			System.out.println(s);
		}
	}
}

class Student {
	private String name;
	private int age;
	public Student() {
		super();
	}
	public Student(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 "Student [name=" + name + ", age=" + age + "]";
	}
	@Override
	public int hashCode() {
		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) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
}
程序运行结果
Student [name=lisi, age=20]
Student [name=zhangsan, age=18]
Student [name=wangwu, age=30]
TreeSet

概述

基于 TreeMapNavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。

特点

  1. TreeSet 是一个有序的并且可排序的集合,它继承于AbstractSet抽象类,实现了NavigableSet, Cloneable, java.io.Serializable接口。

  2. TreeSet是基于TreeMap实现的。TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。

  3. TreeSet是非同步的,线程不安全的,效率高。

  4. 二叉树保证元素唯一

    ​ 【

    ​ 第一个元素进来作为根节点存储

    ​ 后面元素进来和根节点比较

    ​ 大了,放在元素的右边

    ​ 小了,放在元素的左边

    ​ 相等,设置原值

  5. 二叉树保证元素可排序 --> 利用二叉树中序遍历取元素的特点。

底层源码分析示例

// 使用TreeSet无参构造方法实现自然排序
TreeSet<Integer> ts = new TreeSet<>();
// 使用TreeSet带Comparator接口参数的构造方法实现比较其排序
// TreeSet<Student> ts = new TreeSet<>(new StudentComparator());

ts.add(40);
ts.add(38);
ts.add(43);
ts.add(42);
ts.add(37);
ts.add(44);
ts.add(39);
ts.add(38);
ts.add(44);

class TreeSet {

	private transient NavigableMap<E,Object> m; 
	private static final Object PRESENT = new Object(); 

	public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    
    // Comparator<? super E> comparator = new StudentComparator();
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    
    // NavigableMap<E,Object> m = new TreeMap<>(comparator);
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    
    // NavigableMap<E,Object> m = new TreeMap<E,Object>();
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
   			
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
    
}

class TreeMap implements NavigableMap<K,V> {
	private transient Entry<K,V> root;
	private final Comparator<? super K> comparator; 

	public TreeMap() {
        comparator = null;
    }
    // Comparator<? super K> comparator = new StudentComparator();
    public TreeMap(Comparator<? super K> comparator) {
        this.comparator = comparator;
    }
    			
    public V put(K key, V value) {
        Entry<K,V> t = root; 
        // 如果第一个元素进来,作为根节点存储
        if (t == null) {
            compare(key, key); // type (and possibly null) check
			// 创建根节点
            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        // 创建父节点
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator; // cpr = null
        if (cpr != null) {
        	// 比较器排序
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
        	// 自然排序
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key; 38 Integer
            do {
            // 根节点作为父节点
                parent = t;
                cmp = 38 k.compareTo(t.key 40); -1
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
}

class Integer implements Comparable<Integer>{

	public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }

    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
}

interface Comparable {
	public int compareTo(T o);
}

interface Comparator {
	int compare(T o1, T o2);
}

排序标准模板写法代码实例

/*
 * 2.键盘录入5个学生信息(姓名,年龄,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台
 *	注:总分相同等情况下按照语文成绩排序,其次是数学成绩、英语成绩、年龄、姓名
 */
public class TreeSetDemo04 {
	public static void main(String[] args) {
        // 匿名内部类实现Comparator接口方式创建TreeSet对象
		TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {

			@Override
			public int compare(Student s1, Student s2) {
        // 排序标准模板写法
		Collator cmp = Collator.getInstance(java.util.Locale.CHINA);
		double num1 = s1.getTotalScore() - s2.getTotalScore();
		double num2 = (num1 == 0) ? s1.getChineseScore() - s2.getChineseScore() :num1;
		double num3 = (num2 == 0) ? s1.getMathScore() - s2.getMathScore() : num2;
		double num4 = (num3 == 0) ? s1.getEnglishScore() - s2.getEnglishScore() : num3;
		double num5 = (num4 == 0) ? s1.getAge() - s2.getAge() : num4;
		double num6 = (num5 == 0) ? cmp.compare(s1.getName(), s2.getName()): num5;
		return (num6 < 0) ? -1 : ((num6 == 0) ? 0 : 1);
			}
		});
		
		for (int i = 0; i < 30; i++) {
			String name = RandomValue.getChineseName();
			int age = RandomValue.getRandomNum(18, 20);
			double chineseScore = RandomValue.getRandomNum(88, 90);
			double mathScore = RandomValue.getRandomNum(90, 91);
			double englishScore = RandomValue.getRandomNum(88, 89);
			Student s = new Student(name, age, chineseScore, mathScore, englishScore);
			ts.add(s);
		}
		
		for (Student s : ts) {
			System.out.println(s);
		}
	}
}

class Student {
	private String name;
	private int age;
	private double chineseScore;
	private double mathScore;
	private double englishScore;
    
	此处省略构造方法... 
	
	public double getTotalScore() {
		return this.chineseScore + this.mathScore + this.englishScore;
	}
    
    此处省略get/set方法...
	
}

public class RandomValue {
	public static String base = "abcdefghijklmnopqrstuvwxyz0123456789";
	private static String firstName = "赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶姜戚谢邹喻柏水窦章云苏潘葛奚范彭郎鲁韦昌马苗凤花方俞任袁柳酆鲍史唐费廉岑薛雷贺倪汤滕殷罗毕郝邬安常乐于时傅皮卞齐康伍余元卜顾孟平黄和穆萧尹姚邵湛汪祁毛禹狄米贝明臧计伏成戴谈宋茅庞熊纪舒屈项祝董梁杜阮蓝闵席季麻强贾路娄危江童颜郭梅盛林刁钟徐邱骆高夏蔡田樊胡凌霍虞万支柯咎管卢莫经房裘缪干解应宗宣丁贲邓郁单杭洪包诸左石崔吉钮龚程嵇邢滑裴陆荣翁荀羊於惠甄魏加封芮羿储靳汲邴糜松井段富巫乌焦巴弓牧隗山谷车侯宓蓬全郗班仰秋仲伊宫宁仇栾暴甘钭厉戎祖武符刘姜詹束龙叶幸司韶郜黎蓟薄印宿白怀蒲台从鄂索咸籍赖卓蔺屠蒙池乔阴郁胥能苍双闻莘党翟谭贡劳逄姬申扶堵冉宰郦雍却璩桑桂濮牛寿通边扈燕冀郏浦尚农温别庄晏柴瞿阎充慕连茹习宦艾鱼容向古易慎戈廖庚终暨居衡步都耿满弘匡国文寇广禄阙东殴殳沃利蔚越夔隆师巩厍聂晁勾敖融冷訾辛阚那简饶空曾毋沙乜养鞠须丰巢关蒯相查后江红游竺权逯盖益桓公万俟司马上官欧阳夏侯诸葛闻人东方赫连皇甫尉迟公羊澹台公冶宗政濮阳淳于仲孙太叔申屠公孙乐正轩辕令狐钟离闾丘长孙慕容鲜于宇文司徒司空亓官司寇仉督子车颛孙端木巫马公西漆雕乐正壤驷公良拓拔夹谷宰父谷粱晋楚阎法汝鄢涂钦段干百里东郭南门呼延归海羊舌微生岳帅缑亢况后有琴梁丘左丘东门西门商牟佘佴伯赏南宫墨哈谯笪年爱阳佟第五言福百家姓续";
	private static String girl = "秀娟英华慧巧美娜静淑惠珠翠雅芝玉萍红娥玲芬芳燕彩春菊兰凤洁梅琳素云莲真环雪荣爱妹霞香月莺媛艳瑞凡佳嘉琼勤珍贞莉桂娣叶璧璐娅琦晶妍茜秋珊莎锦黛青倩婷姣婉娴瑾颖露瑶怡婵雁蓓纨仪荷丹蓉眉君琴蕊薇菁梦岚苑婕馨瑗琰韵融园艺咏卿聪澜纯毓悦昭冰爽琬茗羽希宁欣飘育滢馥筠柔竹霭凝晓欢霄枫芸菲寒伊亚宜可姬舒影荔枝思丽 ";
	private static String boy = "伟刚勇毅俊峰强军平保东文辉力明永健世广志义兴良海山仁波宁贵福生龙元全国胜学祥才发武新利清飞彬富顺信子杰涛昌成康星光天达安岩中茂进林有坚和彪博诚先敬震振壮会思群豪心邦承乐绍功松善厚庆磊民友裕河哲江超浩亮政谦亨奇固之轮翰朗伯宏言若鸣朋斌梁栋维启克伦翔旭鹏泽晨辰士以建家致树炎德行时泰盛雄琛钧冠策腾楠榕风航弘";

    /**
     * 随机返回任意范围的整数
     */
	public static int getRandomNum(int start, int end) {
		return (int) (Math.random() * (end - start + 1) + start);
	}

	/**
	 * 随机返回中文姓名
	 */
	public static String getChineseName() {
		String name_sex = "";
		int index = getRandomNum(0, firstName.length() - 1);
		String first = firstName.substring(index, index + 1);
		int sex = getRandomNum(0, 1);
		String str = boy;
		int length = boy.length();
		if (sex == 0) {
			str = girl;
			length = girl.length();
			name_sex = "女";
		} else {
			name_sex = "男";
		}
		index = getRandomNum(0, length - 1);
		String second = str.substring(index, index + 1);
		int hasThird = getRandomNum(0, 1);
		String third = "";
		if (hasThird == 1) {
			index = getRandomNum(0, length - 1);
			third = str.substring(index, index + 1);
		}
		return first + second + third;
	}

}
  1. 注意中文排序可以使用 Collator类处理
  2. 建议使用三目运算符进行排序
  3. 针对返回值是double类型的,可以考虑使用三目转换成int类型的结果
LinkeHashSet

概述

List 接口的链接列表实现。实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现
List接口外,LinkedList类还为在列表的开头及结尾getremoveinsert` 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列双端队列

特点

  1. 底层数据结构是 链表和哈希表
  2. 链表保证元素有序
  3. 哈希表保证元素唯一

代码示例

LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("中国");
lhs.add("美国");
lhs.add("美国");
lhs.add("德国");
System.out.println(lhs);

程序运行结果:
[中国, 美国, 德国]

Map


概述

Map集合提供了集合之间一种映射关系,让集合和集合之间产生关系

特点

  1. 能够存储唯一的列的数据(唯一,不可重复)set
  2. 能够存储可以重复的数据(可重复)Collection
  3. 值的顺序取决于键的顺序
  4. 键和值都是可以存储null元素的
  5. 一个映射不能包含重复的键,每个键最多只能映射到一个值

常用功能

  1. 添加功能
V put (K key, V value)
void putAll(Map<? extends K,? extends V) m)
  1. 删除功能
V remove(Object key)
void clear()
  1. 遍历功能
Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V> entrySet()
  1. 获取功能
V get(Object key)
  1. 判断功能
boolean containsKey(Object key)
boolean ContainsValue(Object value)
boolean isEmpty()
  1. 修改功能

    V put(K key,V value)
    void putAll(Map<? extends K,? extends V> m)
    
  2. 长度功能

int size()

注意:

  1. 键相同表示修改,见不同表示添加
  2. 第一次添加元素,返回结果为null,第二次添加返回上一次的键对应的值,返回"前任"
  3. 键是唯一的,无序的,类似于Set接口,值是可重复的,顺序取决于键的顺序,类似于Collection
  4. 并且键和值存在映射关系,建立了集合之间的关系

Map接口的遍历

Set<K> keySet() //遍历键
Collection<V> values() //遍历值
Set<Map.Entry<K,V>> entrySet()

代码示例:

public class MapDemo02 {
	public static void main(String[] args) {
		Map<String, String> map = new HashMap<>();
		
		map.put("陈羽凡", "白百何");
		map.put("王宝强", "马蓉");
		map.put("贾乃亮", "李小璐");
		map.put("PGone", "李小璐");
		System.out.println(map);
		
		// 遍历键
		Set<String> keys = map.keySet();
		for (String key : keys) {
			System.out.println(key);
		}
		System.out.println("================");
		// 遍历值
		Collection<String> values = map.values();
		for (String value : values) {
			System.out.println(value);
		}
		// 遍历键和值
		// 四种方式
		// 方式一: 
		/*
		 * 1.获取到键的集合
		 * 2.遍历键的集合获取到每一个键
		 * 3.通过键获取值
		 * 4.输出键和值
		 */
		System.out.println("================");
		for (String key : map.keySet()) System.out.println(key + "=" + map.get(key));
		System.out.println("================");
		// 方式二: 
		// Iterator<String> it = map.keySet().iterator();
		Iterator<String> it = map.keySet().iterator();
		while (it.hasNext()) {
			String key = it.next();
			String value = map.get(key);
			System.out.println(key + "=" + value);
		}
		System.out.println("================");
		// 方式三:
		/*
		 * 1.获取 键值对 整体的集合
		 * 2.遍历 键值对对象的集合 ,获取到每一对键值对 Entry<String, String>
		 * 3.通过每一对键值对对象 分别 获取键 获取值
		 * 4.输出即可
		 */
		for (Entry<String, String> keyValue : map.entrySet()) System.out.println(keyValue.getKey() + "=" + keyValue.getValue());
		System.out.println("================");
		// 方式四:
		Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<String, String> keyValue = iterator.next();
			System.out.println(keyValue.getKey() + "=" + keyValue.getValue());
		}
		System.out.println("================");
		// 观察JDK的源码遍历方式
		System.out.println(map);
		/*
		 * public String toString() {
		        Iterator<Entry<K,V>> i = this.entrySet().iterator();
		        if (! i.hasNext())
		            return "{}";
		
		        StringBuilder sb = new StringBuilder();
		        sb.append('{');
		        for (;;) {
		            Entry<K,V> e = i.next();
		            K key = e.getKey();
		            V value = e.getValue();
		            sb.append(key   == this ? "(this Map)" : key);
		            sb.append('=');
		            sb.append(value == this ? "(this Map)" : value);
		            if (! i.hasNext())
		                return sb.append('}').toString();
		            sb.append(',').append(' ');
		        }
		    }
		 */
	}
}
HashMap

特点:

  1. 底层数据结构是哈希表
  2. 元素无序,唯一
  3. 数据结构针对键有效,和值无关
  4. 保证元素唯一依赖的hashCode和equals方法
  5. 如果键是系统类,一般都是重写hashCode和equals方法,如果键是自定义类,需要自己重写

代码示例:

public class HashMapDemo01 {
	public static void main(String[] args) {
		HashMap<Employee, String> hm = new HashMap<>();
		hm.put(new Employee("1001", "张三1", 2600.0), "张三1");
		hm.put(new Employee("1002", "张三2", 2700.0), "张三2");
		hm.put(new Employee("1002", "张三2", 2700.0), "张三2");
		hm.put(new Employee("1003", "张三3", 2800.0), "张三3");
		hm.put(new Employee("1003", "张三3", 2800.0), "张三3");
		
		for(Employee e : hm.keySet()) {
			String value = hm.get(e);
			System.out.println(e + "=" + value);
		}
		
	}
}
HashTable

HashTablele类实际上就是老版的HashMap.

Hashtable与HashMap的区别

  1. HashMapHashtable底层数据结构都是哈希表
  2. HashMap线程不安全,效率高,可以存储nullnull
  3. Hashtable是旧版的HashMap,线程安全,效率低,不可以存储null值、null
TreeMap

特点:

  1. 底层数据结构是自平衡二叉树
  2. 如何保证元素唯一?根据二叉树的存的特点
    • 第一个元素进来作为根节点存储
    • 后面元素和根节点进行比较
    • 大了,放在根的左边
    • 小了,放在根的右边
    • 相等,就不存储
  3. 如何保证元素无序的
    • 因为可排序,所以相对于存储的时候是无序的
  4. 如何保证元素是可排序的
    • 根据二叉树取的特点(中序遍历)
  5. TreeMap的数据结构针对键有效,和值无关
  6. 如果键是系统类一般都实现了Comparable接口,如果是自定义对象,需要自己实现Comparable接口(自然排序)或者Comparator(比较器排序)

代码示例:

/*
 * 键是员工类,值是员工姓名的情况
 */
public class TreeMapDemo02 {
	public static void main(String[] args) {
//		TreeMap<Employee, String> tm = new TreeMap<>();
		TreeMap<Employee, String> tm = new TreeMap<>(new Comparator<Employee>() {
			@Override
			public int compare(Employee e1, Employee e2) {
				// 如何排序?
				// 先按照 编号排序, 编号相等按照工资排序, 工资相等按照姓名排序
				// 三目运算符的标准排序格式: 
				Collator c = Collator.getInstance(Locale.CHINA);
				int cmp = e1.getId().compareTo(e2.getId());
				int cmp2 = (cmp == 0) ? e1.getSalary().compareTo(e2.getSalary()) : cmp;
				int cmp3 = (cmp2 == 0) ? c.compare(e1.getName(), e2.getName()) : cmp2;
				return cmp3;
			}
		});
		tm.put(new Employee("1001", "张三", 2000.0), "张三");
		tm.put(new Employee("1002", "哈哈", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2600.0), "李四");
		tm.put(new Employee("1002", "呵呵", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2100.0), "李四");
		tm.put(new Employee("1002", "李四", 2700.0), "李四");
		tm.put(new Employee("1002", "嘿嘿", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2000.0), "李四");
		tm.put(new Employee("1002", "娃娃", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2000.0), "李四");
		tm.put(new Employee("1002", "李四", 2000.0), "李四");
		tm.put(new Employee("1007", "刘九", 2000.0), "刘九");
		tm.put(new Employee("1008", "王十", 2000.0), "王十");
		tm.put(new Employee("1009", "肖十一", 2000.0), "肖十一");
		tm.put(new Employee("1009", "肖十一", 2000.0), "肖十一");
		tm.put(new Employee("1003", "王五", 2000.0), "王五");
		tm.put(new Employee("1003", "王五", 2000.0), "王五");
		tm.put(new Employee("1006", "钱八", 2000.0), "钱八");
		tm.put(new Employee("1004", "赵六", 2000.0), "赵六");
		tm.put(new Employee("1005", "孙七", 2000.0), "孙七");
		tm.put(new Employee("1006", "钱八", 2000.0), "钱八");
		
		for(Employee e : tm.keySet()) {
			String name = tm.get(e);
			System.out.println(e + "==" + name);
		}
		
	}
}

LinkedHashMap

特点:

  1. 底层数据结构是链表+哈希表
  2. 链表保证元素有序,哈希表保证元素唯一
public class LinkedHashMapDemo01 {
	public static void main(String[] args) {
		LinkedHashMap<Employee, String> hm = new LinkedHashMap<>();
		hm.put(new Employee("1001", "张三1", 2600.0), "张三1");
		hm.put(new Employee("1002", "张三2", 2700.0), "张三2");
		hm.put(new Employee("1002", "张三2", 2700.0), "张三2");
		hm.put(new Employee("1003", "张三3", 2800.0), "张三3");
		hm.put(new Employee("1003", "张三3", 2800.0), "张三3");
		
		for(Employee e : hm.keySet()) {
			String value = hm.get(e);
			System.out.println(e + "=" + value);
		}
	}
}
WeakHashMap

WeakHashMap表示以弱键形式存在的哈希表

弱键:垃圾对象作为键

数据结构是哈希表

如果键是以弱键形式存在,会被垃圾回收器回收该键,导致该值也被移除

代码示例:

public class WeakHashMapDemo01 {
	public static void main(String[] args) {
		WeakHashMap<String, String> whm = new WeakHashMap<>();
		/*whm.put("abc", "123");
		whm.put("efg", "456");
		whm.put("efg", "456");
		whm.put("hij", "789");*/
		
		whm.put(new String("abc"), "123");
		whm.put(new String("efg"), "456");
		whm.put(new String("hij"), "789");
		whm.put("opq", "789");
		
		System.gc();
		System.runFinalization();
		
		System.out.println(whm);
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值