当前位置:主页 > 查看内容

你能区分这几种Java集合类型?

发布时间:2021-04-07 00:00| 位朋友查看

简介:Java集合的背景 ⒈Java知识中有一个类集框架的概念每种文献各有各的说法也有的叫做Java集合类造成混乱。并且概念中Collection翻译成中文又是类集的意思Set翻译成中文是集合的意思混乱的翻译、指代不明的名词概念以及网络上copy的差错导致初学者从入门到放弃……

Java集合的背景

	⒈Java知识中有一个类集框架的概念,每种文献各有各的说法,也有的叫做Java集合类,造成混乱。
	并且概念中Collection翻译成中文又是类集的意思,Set翻译成中文是集合的意思,
	混乱的翻译、指代不明的名词概念以及网络上copy的差错,导致初学者从入门到放弃。
	现在本学习笔记将帮你梳理基础概念、供你参考解决疑惑。
	
	⒉JDK 1.2版本开始引入类集开发框架,提供一系列接口和实现子类,最初时JDK版本都是使用
	Object类型实现数据接收(导致可能会存在ClassCastException抛异常安全隐患),随技术提高
	增加泛型进行规定,才能使算法有良好的提高

了解Java集合类

?什么是集合?

集合框架:用于存储数据的容器

1.2集合与数组的区别

(1)长度区别:集合长度可变,数组长度不可变

(2)内容区别:集合可存储不同类型元素,数组存储只可单一类型元素

(3)元素区别:集合只能存储引用类型元素,数组可存储引用类型,也可存储基本类型

  • 首先让我们了解了解Java集合类之间的关系吧
    在这里插入图片描述
    Java各集合类之间的关系

      ①Java集合要从两大接口说起,一为Collection接口,二为Map接口,都继承了Iterable接口,它们是同一个层次的。
    
      ②Collection接口被List接口和Set接口继承;
    
      ③List接口有三个实现类,ArrayList,LinkedList,Vector;
    
      ④Set接口被HashSet类实现,被SortedSet接口继承,同时TreeSet类实现SortedSet接口,LinkedHashSet类继承	
      HashSet类;
    
      ⑤Map接口有两个实现类,HashMap,HashTable,同时Propertise类继承HashTable;
      
      ⑥Map接口被SortedMap接口继承,同时TreeMap类实现了SortedMap接口;
    

使用类集框架优点

	?使用核心集合类降低开发成本,而非实现我们自己的集合类,易扩展或修改。
	
    ?随着使用经过严格测试的集合框架类,代码质量会得到提高。
    
    ?通过使用JDK附带的集合类,可以降低代码维护成本。
    
	?复用性和可操作性。

    ?允许不同类型的类集以相同的方式和高度互操做方式工作。

对集合类简单介绍

1. Collection接口()

㈠Collection接口为单列集合的顶层接口,含有允许常见操作。

//添加方法:
add(Object o) //添加指定元素
addAll(Collection c) //添加指定集合

//删除方法:
remove(Object o) //删除指定元素
removeAll(Collection c) //输出两个集合的交集
retainAll(Collection c) //保留两个集合的交集
clear() //清空集合

//查询方法:
size() //集合中的有效元素个数
toArray() //将集合中的元素转换成Object类型数组

//判断方法:
isEmpty() //判断是否为空
equals(Object o) //判断是否与指定元素相同
contains(Object o) //判断是否包含指定元素
containsAll(Collection c) //判断是否包含指定集合

Collection接口的常用子类
在这里插入图片描述

1.1 List集合

? List 子接口的特点:
	? 元素可重复性;  // List接口的最大特点;
	? 有索引,可进行精准的操作某特定元素;
	? 元素有序性,存储及取出的顺序一致;
	? 可以存多个null;

List接口扩充的方法

List 子接口的定义:public interface List<E> extends Collection<E>

在这里插入图片描述
注:get方法比较常用

?List常见的子类
在这里插入图片描述
?List和Vector的比较
在这里插入图片描述
范例:

	import java.util.List;
	import java.util.Arrays;
	 
	class Solution {
	    public static void main(String[] args) {
	    	//List<Integer> ls1 = Arrays.asList(1, 2, null);
	    	List<String> ls2 = List.of("JieWen","2","King");
			Object result[] = ls2.toArray();//将List转为Array储存
			//System.out.println(ls1.contains(null));
	    	for(Object temp :  result){
	    				System.out.printf(temp + "\");
			}
	    }
	}
	//结果:JieWen、2、King

1.1.1 ArrayList 子类

 ?线性结构最方便的实现就是基于数组实现;

ArrayList的类定义:

public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

你了解这三种构造方法?

public ArrayList() {
        this(10);
        //调用ArrayList(10) 默认初始化一个大小为10的object数组。
    }

public ArrayList(int initialCapacity) {    
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
         //如果用户初始化大小小于0抛异常,否则新建一个用户初始值大小的object数组。                                      
        this.elementData = new Object[initialCapacity];
    } 

public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // 当c.toArray返回的不是object类型的数组时,进行下面转化。
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
/*默认情况下使用ArrayList会生成一个大小为10的Object类型的数组。
也可以调用ArrayList(int initialCapacity) 来初始化Object数组的大小。
并且用户可以往ArrayList中传入一个容器只要这个容器是Collection类型的。
调用ArrayList(Collection<? extends E> c)接口的时候会将容器数组化处理并
将这个数组值赋给Object数组。*/

⑴ ArrayList的特点:

?ArrayList 基于索引的,因此可以随机访问;
?ArrayList 不太适合删除操作,删除元素将导致大量的数据移动;
?ArrayList 可以包含重复的元素;
?ArrayList 是非线程安全的;
?ArrayList 维护了元素插入时的顺序。

⑵ ArrayList的底层分析

①数组结构是有序的元素序列,在内存中开辟一段连续的空间,在空间中存放元素,每个空间都有编号,
通过编号可以快速找到相应元素,因此查询快;

②数组初始化时长度是固定的,查询快、要想增删元素,动态开辟空间、必须创建一个新数组,把源数组的元素复制进来,随后源数组销毁,耗时长,因此增删慢。

⑶ArrayList 常见方法:

1.add(E e) 在集合末尾新增一个元素
2.add(int index, E element) 在指定位置添加元素
3.get(int index)获取指定位置的元素
4.remove(int index) 删除指定位置的元素
5.remove(Object o) 删除指定元素
6.indexOf(Object o) 查询指定元素的位置 lastIndexOf也一样,只是从尾部开始遍历
7.set(int index, E element) 设置指定位置的元素值
8.retainAll(Collection<?> c) 求两个集合的交集

⑷ArrayLIst 实现:

import java.util.*;
public class Daycare{
   public static void main(String[] args){
	   //ArrayList  Collections.addAll(集合,元素,....)
	   //创建一个集合对象装名字
	   ArrayList<String> list=new ArrayList<>();
	   //一次添加一个元素的方式:添加:Andy   Lee
	   Collections.addAll(list,"Andy","Lee");
	   //统计集合里面有几个人的姓名
	   System.out.println("人的个数:"+list.size());
	   //打印第一个人的姓名
	   System.out.println("第一个人的名字:"+list.get(0));
	   //判断集合里面是否出现Lee的名字
	   System.out.println(list.contains("Lee"));
	   //使用两种不同的方式打印 所有以A开头的名字
	   for(String name:list){
		   if(name.charAt(0)=='A'){
			   System.out.println(name);
			   }
		   }
       for(String name:list){
		   if(name.startsWith("A")){
			   System.out.println(name);
			   }
		   }
      //用迭代器
      for(Iterator<String> car=list.iterator();car.hasNext();){
		  String name=car.next();
		  if(name.startsWith("A")){
			  System.out.println(name);
			  }

		  }
	   }
}

(5)ArrayList 保存自定义类对象

必须实现的方法:

1、要在自定义类中重写equals()方法
2、在实际开发之中 remove()contains(Object o)都是很少用的
3、主要用到的还是 add() 方法与 get()方法
4、在重写equals()方法的时候要判断3个为空,之后转型
	[1.是否是自己 2.是否为空 3.是否为本类(instanceof判断)]

方法的实现代码:

import java.util.ArrayList;

/**
 * ArrayList保存自定义类对象
 **/
public class ArrayListSaveDefineClassInstance {
    public static void main(String[] args) {
    
        ArrayList<ArrayListPerson> all= new ArrayList<ArrayListPerson> ();
        all.add(new ArrayListPerson("张三" , 19));
        all.add(new ArrayListPerson("李四" , 20));
        all.add(new ArrayListPerson("王五" , 12));

        //由于地址不一样,无法找到也无法,删除,所以要在自定义类中复写equals()方法
        System.out.println(all.contains(new ArrayListPerson("王五" , 12)));
        all.remove(new ArrayListPerson("王五" , 12));

        System.out.println(all);
    }
}


/**
 * 自定义类
 **/
class ArrayListPerson{
    private  String name;
    private  int age;
    //需要覆写的equals()方法才能保证能够实现查找
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof ArrayListPerson)) {
            return false;
        }

        if (this == obj) {
            return false;
        }

        if (obj == null) {
            return false;
        }

        ArrayListPerson objP = (ArrayListPerson) obj;
        return this.name==objP.name && this.age==objP.age;
    }

    @Override
    public String toString() {
        return "ArrayListPerson{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public ArrayListPerson(String name , int age) {
        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;
    }
}

1.1.2 LinkedList 子类
?概述

	LinkedList 是 Java 集合中比较常用的数据结构,与 ArrayList 一样,实现了 List 接口,
	只不过 ArrayList 是基于数组实现的,而 LinkedList 是基于链表实现的。所以 LinkedList 
	插入和删除方面要优于 ArrayList,而随机访问上则 ArrayList 性能更好。除了 LIst 接口
	之外,LinkedList 还实现了 Deque,Cloneable,Serializable 三个接口。这说明该数据
	结构支持队列,克隆和序列化操作的。与 ArrayList 一样,允许 null 元素的存在,且是
	不支持多线程的。

?LinkedList 特点

		1.数据结构由双向链表实现
		2.它不存在容量不足的问题
		3.因为是链表无下标,所以查询慢、但是增删快。

?构造方法

public LinkedList() {}
public LinkedList(Collection<? extends E> c) {
    this();
    addAll(c);
}

LinkedList 提供了两个构造方法:LinkedList() 和 LinkedList(Collection<? extends E> c)。

?LinkedList 实现原理:

LinkedList是基于双向链表的数据结构实现的,链表是可以占用一段不连续的内存,双向链表有前驱节

点和后驱节点,里面存储的是上一个元素和后一个元素所在的位置,中间的黄色部分就是业务数据了,

当我们需要执行插入的任务,比如第一个元素和第二个元素之间,只需要改变他们的前驱节点和后驱节

点的指向就可以了,不要像动态数组那么麻烦,删除也是同样的操作,但是因为是不连续的内存空间,

当需要执行查找,需要从第一个元素开始查找,直到找到我们需要的数据。

在这里插入图片描述
?LinkedList 常用方法:

	增加:
	add(E e):在链表后添加一个元素;   通用方法
	addFirst(E e):在链表头部插入一个元素;  特有方法
	addLast(E e):在链表尾部添加一个元素;  特有方法
	push(E e):与addFirst方法一致  
	offer(E e):在链表尾部插入一个元素        
	add(int index, E element):在指定位置插入一个元素。                                                                                                                                          				       
	offerFirst(E e):JDK1.6版本之后,在头部添加; 特有方法 
	offerLast(E e):JDK1.6版本之后,在尾部添加; 特有方法
	
	删除:
	remove() :移除链表中第一个元素;    通用方法  
	remove(E e):移除指定元素;   通用方法
	removeFirst(E e):删除头,获取元素并删除;  特有方法
	removeLast(E e):删除尾;  特有方法
	pollFirst():删除头;  特有方法
	pollLast():删除尾;  特有方法
	pop():和removeFirst方法一致,删除头。 
	poll():查询并移除第一个元素     特有方法    
	
	查:
	get(int index):按照下标获取元素;  通用方法
	getFirst():获取第一个元素;  特有方法
	getLast():获取最后一个元素; 特有方法
	peek():获取第一个元素,但是不移除;  特有方法
	peekFirst():获取第一个元素,但是不移除; 
	peekLast():获取最后一个元素,但是不移除;
	pollFirst():查询并删除头;  特有方法
	pollLast():删除尾;  特有方法
	poll():查询并移除第一个元素     特有方法

?LinkedList 的实现方法:

public class LinkedListMethodsDemo {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
 
        linkedList.add("first");
        linkedList.add("second");
        linkedList.add("third");
        System.out.println(linkedList);
 
        linkedList.addFirst("addFirst");
        System.out.println(linkedList);
 
        linkedList.addLast("addLast");
        System.out.println(linkedList);
 
        linkedList.add(2, "addByIndex");
        System.out.println(linkedList);
    }
}
/*输出结果:
[first, second, third]
[addFirst, first, second, third]
[addFirst, first, second, third, addLast]
[addFirst, first, addByIndex, second, third, addLast]*/

1.1.3 Vector 子类

注:Vector类在JDK1.0就已经提供,但是由于以下:
1.因为vector是线程安全的,所以效率低,这容易理解,类似StringBuffer
2.Vector空间满了之后,扩容是一倍,而ArrayList仅仅是一半
3.Vector分配内存的时候需要连续的存储空间,如果数据太多,容易分配内存失败
4.只能在尾部进行插入和删除操作,效率低

所以vector子类目前已经过时,但是由于其广泛性并没有弃用。

?Vector底层分析:

底层数据结构:数组实现,和ArrayList 一样,但是Vector 是同步方法线程安全,但是效率低,所以不推荐使用,只有要求需要线程安全才会去使用该子类;

?Vector 所定义结构

java.lang.Object 
    java.util.AbstractCollection<E> 
        java.util.AbstractList<E> 
            java.util.Vector<E> 

范例:

public class VectorTest{
	public static void main(String[] args){
		List<String> all = new Vector<String>();
		all.add("JieWen");
		System.out.printf(all);
	}
}

1.2 Set 集合

?提供该接口原因:
为了与List接口的使用有所区分,在进行Set接口设计的时候要求内部不允许保存重复的元素,Set接口定义如下:

public interface Set<E> extends Collection<E>(){}

在这里插入图片描述

Set接口的使用

public class SetTest{
	public static void main(String[] args){
		Set<String> all = new Set.of("JieWen","XiaoFei");
		System.out.printf(all);

?Set的特性

A:存入集合的顺序和取出集合的顺序不一致

B:没有索引,所以不能使用普通for循环,可Foreach循环。

C:存入集合的元素没有重复(如果重复会抛出异常)特点

? 基本和Collection接口的方法一种,没有进行功能的扩充。
?Set的子类
在这里插入图片描述
Set常用的方法

 Set 接口继承 Collection 接口,而且它不允许集合中存在重复项。所有原始方法都是现成的,
 没有引入新方法。具体的 Set 实现类依赖添加的对象的 equals() 方法来检查等同性。
 
   1. public int size() :返回set中元素的数目,如果set包含的元素数大于Integer.MAX_VALUE,返回Integer.MAX_VALUE;

   2. public boolean isEmpty() :如果set中不含元素,返回true ;
   3.public boolean contains(Object o) :如果set包含指定元素,返回true ;
   4.public Iterator iterator(): 返回set中元素的迭代器,元素返回没有特定的顺序,除非set提高该保证的某些类的实例 ;
   5. public boolean add(Object o) :如果set中不存在指定元素,则向set加入;
   6.public boolean remove(Object o) :如果set中存在指定元素,则从set中删除 ;
   7.public boolean removeAll(Collection c) :如果set包含指定集合,则从set中删除指定集合的所有元素 ;
   8.public void clear() :从set中删除所有元素;

1.2.1 HashSet实现类
数据结构:
JDK 1.8之前是通过哈希表的单向链表和数组来组成、JDK 1.8之后将单链表该为(单链表+红黑树)目的在于:当链表的长度超过阀值(8)时改为红黑树、而链表查找元素的时间复杂度为 O(n),远远大于红黑树的 O(logn),尤其是在节点越来越多的情况下,O(logn) 体现出的优势会更加明显;简而言之就是为了提升查询的效率。

■HashSet 的特性:(散列存放)

 1.元素的无序性 != 随机性。真正的无序性,是指元素在底层存储的位置是无序的。
 (哈希表通过 hashcode 哈希值和 equals() 方法进行判断保证独一性)

 2.不可重复性。当向Set集合中添加相同元素的时候,后面的这个添加不进去。程序不会报错!
	
 3.Set集合没有自己独有的方法,它的方法都是继承于Collection接口中的现有方法

 4.HashSet类是非线程安全的

 5.允许集合元素值为null

■HashSet的构造方法

public class HashSet<E>   
 extends AbstractSet<E>   
 implements Set<E>, Cloneable, java.io.Serializable  {}

?方法概览

1.add(Object o)和addAll(Collection c)增加元素
2.contains(Object o)和containsAll(Collection c)判断元素是否存在
3.isEmpty()判断集合是否为空
4.remove(Object o)和removeAll(Collection c)删除元素
5.size()返回集合的大小
6.clear()清空集合
7.iterator()迭代器
8.toArray()将内容转到数组中

范例:

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

public class Test {
	public static void main(String args[]) {
		int[] a= {6,5,4,3,2,1,1,5,6,7};
		HashSet<Integer> hs=new HashSet<>();       //所有的集合中存储的元素的类型都不会是基本数据类型!!!使用菱形推断创建对象
		for(int i:a) {
			if(!hs.add(i))
				System.out.println(i+"已存在!");       //HashSet中的元素不允许重复!
			hs.add(i);
		}
		/*for(int i=0;i<a.length;i++) {
			if(!hs.add(a[i]))
				System.out.println(a[i]+"已存在!");
			hs.add(a[i]);
		}*/
		if(!hs.isEmpty()) {                 //判断集合是否为空
			System.out.println("hs不为空");
		}
		if(hs.contains(7)) {               //判断集合是否包含元素
			System.out.println("集合hs中包括元素7");
		}
		for(int j : hs) {
			System.out.print(j+"  ");
		}
		System.out.println();
		HashSet<Integer> h=new HashSet<>();
		h.add(102);           //添加元素102
		//h.remove(102);        删除元素102
		h.add(103);
		h.add(2);
		if(hs.containsAll(h)) {           //判断集合是否包含另一集合的所有元素
			System.out.println("hs集合中包含h集合中的所有元素");
		}
		else {
			System.out.println("hs集合中不包含h集合中的所有元素");
		}
		//hs.addAll(h);            将集合h中的所有元素添加到hs中
		hs.removeAll(h);         //删除hs和h的交集中的元素
		//hs.retainAll(h);         取得hs和h的交集的元素
		//hs.clear();              删除所有hs的元素
		Object[] objects=hs.toArray();    //HashSet.toArray()的无参方法
		for(Object o:objects) {
			System.out.print(o+"  ");
		}
		System.out.println();
		Integer[] mid=hs.toArray(new Integer[hs.size()]);           //HashSet.toArray()的带参方法
		for(Integer integer:mid) {
			System.out.print(integer+"  ");
		}
		System.out.println();
		hs.removeAll(java.util.Collections.singleton(7));     //Collections.singleton(7):生成只有一个元素7的集合
		Iterator<Integer> iterator=hs.iterator();
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+"  ");
		}
		System.out.println();	
	}
}

1.2.2 TreeSet 子类

?数据结构:红黑树

?TreeSet特性

1.TreeSet中存储的类型必须是一致的,不能一下存int,一下又存string
2、TreeSet在遍历集合元素时,是有顺序的【从小到大】(ASCLL码排序)
3、排序:当向TreeSet中添加自定义对象时,有2种排序方法,1:自然排序 2、定制排序
自然排序:要求自定义类实现java.lang.Comparable接口并重写compareTo(Object obj)方法。
在此方法中,指明按照自定义类的哪个属性进行排序

?底层分析

TreeSet的底层是TreeMap,添加的数据存入了map的key的位置,而value则固定是PRESENT。TreeSet中的元素是有序且不重复的,因为TreeMap中的key是有序且不重复的。
注意: 不允许存放null。

范例:

public class TreeTest{
    public static void main(String[] srgs){
    Set<String> all = new Set<>();
    all.add("JieWen");
    System.out.printf(all);
	}
}

?TreeSet 子类排序分析

?即类要实现Comparable接口,并重写compareTo()方法,TreeSet对象调用add()方法时,会将存入的对象提升为Comparable类型,然后调用对象中的compareTo()方法进行比较,判断有没有重复元素,根据比较的返回值进行存储。
因为TreeSet底层是二叉树,当compareTo方法返回0时,不存储;当compareTo方法返回正数时,存入二叉树的右子树;当compareTo方法返回负数时,存入二叉树的左子树。如果一个类没有实现Comparable接口就将该类对象存入TreeSet集合,会发生类型转换异常。

?自然排序

import java.util.Set;
import java.util.TreeSet;
//TreeSet的自然排序
//1.实体类实现Comparable接口,重写compareTo()方法
public class TestTreeSetSort1 {
	public static void main(String[] args) {
		Set<Student> tree=new TreeSet();
		Student s1=new Student("JieWen",21,88);
		Student s2=new Student("Feign",21,34);
		Student s3=new Student("K",24,99);
		Student s4=new Student("J",27,13);
		Student s5=new Student("N",22,65);
		tree.add(s1);
		tree.add(s2);
		tree.add(s3);
		tree.add(s4);
		tree.add(s5);
		//Exception in thread "main" java.lang.ClassCastException: com.qianfeng.kxf.day25.set.Student cannot be cast to java.lang.Comparable
		//为什么报错?因为添加的是对象元素,必须实现一个排序,否则就会类型转换错误的问题
		System.out.println("元素对象多少个"+tree.size());
		for(Student s:tree) {
			System.out.println(s);
		}
		
	}
}
class Student implements Comparable{
	private String name;
	private int age;
	private int score;
	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;
	}
	public int getScore() {
		return score;
	}
	public void setScore(int score) {
		this.score = score;
	}
	@Override
	public String toString() {
		return "Student [name=" + name + ", age=" + age + ", score=" + score + "]";
	}
	public Student(String name, int age, int score) {
		super();
		this.name = name;
		this.age = age;
		this.score = score;
	}
	public Student() {
		super();
	}
	
	/*//重写该方法:只通过名字进行排序
	@Override
	public int compareTo(Object o) {
		//先转换为Student类型对象
		Student str=(Student)o;
		//先对姓名进行排序,直接调用String类型中的compareTo()方法进行比较
		return this.name.compareTo(str.name);
	}
//	元素对象多少个3
//	Student [name=J, age=27, score=13]
//	Student [name=K, age=24, score=99]
//	Student [name=JieWen, age=21, score=88]
*/	
	
	/*//如果名字相同,就按年龄排序
	public int compareTo(Object o) {
		Student stu=(Student)o;
		//先对名字排序:升序
		int n=this.name.compareTo(stu.name);
		//如果名字相同就比较年龄 :升序
		return n==0?this.age-stu.age:n;
	}*/
	//如果名字相同,就按年龄排,如果年龄还相同就按成绩排
	public int compareTo(Object o) {
		Student stu=(Student)o;
		int n=this.name.compareTo(stu.name);
		int a=n==0?this.age-stu.age:n;
		return a==0?this.score-stu.score:a;
	}
}

?定制排序

//主要实现方法
class MyComparator implements Comparator<Person>{
	
	//按姓名排序
	/*@Override
	public int compare(Person o1, Person o2) {
		int n=o1.getName().compareTo(o2.getName());
		return n;
	}*/
	
	//按姓名和年龄排序
	/*public int compare(Person p1,Person p2) {
		int n=p1.getName().compareTo(p2.getName());
		return n==0?p1.getAge()-p2.getAge():n;
	}*/
	
	//按姓名.年龄.成绩排序
	public int compare(Person p1,Person p2) {
		int n=p1.getName().compareTo(p2.getName());
		int a=n==0?p1.getAge()-p2.getAge():n;
		return a==0?p1.getScore()-p2.getScore():a;
	}
}

?消除重复元素

由于TreeSet有排序要求,所以利用Comparable接口实现重复元素判断,而非排序集合
中对重复元素的判断Object类提供的hash码,boolean equals()对象比较。

代码实现

@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        People people = (People) o;
        return Objects.equals(name, people.name) &&
                Objects.equals(age, people.age);
    }

    @Override
    public int hashCode() {

        return Objects.hash(name, age);
    }

    @Override
    public int compareTo(People o) {
        if(this.age > o.age){
            return 1;
        }else if(this.age < o.age){
            return -1;
        }else {
            return this.name.compareTo(o.name);
        }
    }

1.2.3 LinkedHashSet 实现类

目的:为了解决HashSet实现类的无序性而创建的一个类,实现基于链表的顺序储存

数据结构:和HashSet基本一致,只是将单链表改为双向链表

?LinkedHashSet 特性:

>查询快、元素有序、元素不可重复、没有索引。

底层分析:

为HashSet的子类,只是比他多添加一条记录元素顺序的链表,所以其为有序;

 
    //从键盘输入字符串,去掉重复字符输出
    public static void getSingleChar(){
        Scanner sc=new Scanner(System.in);
        System.out.println("请输入一串字符:");
        String str=sc.nextLine();
        char[] ch=str.toCharArray();
        LinkedHashSet<Character> linkedHashSet=new LinkedHashSet<>();
        for (char c:ch) {
            linkedHashSet.add(c);
        }
        for (Character c:linkedHashSet) {
            System.out.println(c);
        }
 
    }

1.3 Map 子类

?概述

Map是STL的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。这里说下map内部数据的组织,map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的;
(key,value)即键值对,key不允许重复,value允许重复,一一对应;
Map属于双列集合,保证key唯一,必须重写hashCode 和equals()方法;

?Map常用方法

添加、删除、修改操作:
1.Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
2.void putAll(Map m):将m中的所有key-value对存放到当前map中
3.Object remove(Object key):移除指定key的key-value对,并返回value
4.void clear():清空当前map中的所有数据

元素查询的操作:
1.Object get(Object key):获取指定key对应的value
2.boolean containsKey(Object key):是否包含指定的key
3.boolean containsValue(Object value):是否包含指定的value
4.int size():返回map中key-value对的个数
5.boolean isEmpty():判断当前map是否为空
6.boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法:
1.Set keySet():返回所有key构成的Set集合
2.Collection values():返回所有value构成的Collection集合
3.Set entrySet():返回所有key-value对构成的Set集合

注:JDK1.9后为了方便进行Map数据的操作,添加了Map.of()的方法接受每一组数据转为Map;

public class MapTest{
    public static void main(String[] srgs){
    //one =1,two =2;
    Map<String,Integer> map = Map.of("one",1,"two",2);//k,v型
    System.out.printf(map);
    //{two=2},{one=1}
	}
}

注意:

⒈如果设置的key重复,则程序抛出IllegalArgumentException异常,提示key重复;
2.如果设置的key或value为null,则抛出NullPointerException异常;

?Map和Collection之间的区别

↘Collection接口是目的为了设置内容再 输出
↘Map接口设置完内容再 查找

1.3.1 HashMap 子类

?概述

HashMap 是基于哈希表的 Map 接口的实现,以 Key-Value 的形式存在,即存储的对象是 Entry (同时包含了 Key 和 Value) 。在HashMap中,其会根据hash算法来计算key-value的存储位置并进行快速存取。特别地,HashMap最多只允许一条Entry的键为Null(多条会覆盖),但允许多条Entry的值为Null。此外,HashMap 是 Map 的一个非同步的实现。不同步导致可能多个线程同时写该方法;

?数据结构:
JDK1.8 之前哈希表(数组+单向链表);JDK 1.8之后(数组+单向链表+红黑树);当链表长度超过阀值时链表转为红黑树;原因和HashSet一致;HashMap的底层构造和HashSet一致;

?HashMap的特性

1、允许key/value为null,但是key只能有一个null

2、非线程安全,多个线程同时操作同一个HashMap实例所做的修改在线程间不同步

3、遍历时不保证任何顺序,跟元素插入顺序或者访问顺序无关

4、进行遍历时如果执行HashMap的remove(Object key)或者put(Object
value)方法时会快速失败,抛出异常ConcurrentModificationException。遍历时删除元素只能通过Iterator本身的remove()方法实现。

?HashMap常用方法

1.put(Object key,Object value)在此映射中关联指定的Key-value;
2.putAll(Collection c)在此映射中将指定的映射关系添加到被操作的映射中;
3.get(Object key)根据key获取指定的value;
4.containsKey(Object key)检测该映射中是否存在指定key的映射,有则返回true;没有则返回false;
5.containsValue(Object value)检测该映射中是否存在指定value的映射,有则返回true;没有则返回false;
6.remove(Object key)根据key的值删除指定的映射关系;
7.isEmpty()测试映射是否为空;
8.values()返回值的集合;

?HashMap方法实现

public class HashMap{
    public static void main(){
		Map<String,Integer> map = new HashMap<>();
		map.put("one",1);
		map.put("two",2);
		map.put("null",0);
		map.put("one",101);//就近原则覆盖前面的
		System.out.printf(map.get("ten"));不存在则返回null;
	}
}

?迭代器输出

Map<String, String> map = new HashMap<>();
       
	Iterator iter = map.entrySet().iterator();//比Set多一步
	while (iter.hasNext()) {
		Map.Entry entry = (Map.Entry) iter.next();
		System.out.println("[Key] : " + entry.getKey() + " [Value] : " + entry.getValue());
	}

1.3.2 LinkedHashMap 子类

数据结构:
和HashMapt基本一致,只是将单链表改为双向链表

?LinkedHashMap 特性:

迭代HashMap的顺序并不是HashMap放置的顺序,也就是无序。而LinkedHashMap,它虽然增加了时间和空间上的开销,通过一个双向链表,LinkedHashMap保证了元素迭代的顺序。该迭代顺序有两种,可以是插入顺序或者是访问顺序。LinkedHashMap继承了HashMap类,有着HashMap的所有功能,还提供了记住元素添加顺序的方法,通过链表实现;
key不可重复但是可以为null;value可以重复;

?LinkedHashMap 定义:

 public class LinkedHashMap<K,V> 
    extends HashMap<K,V>
    implements Map<K,V>{}

LinkedHashMap方法实现和上面 HashMap 基本一致。

1.3.3 Hashtable 子类
?概述

早期的字典实现类(通过哈希值查找),可以更方便实现数据查询;但是这个子类比较古老,目前已经不推荐使用该类,因为该方法是同步方法,线程安全但是效率比较低;同时Hashtable不允许储存null数据,无序;

? Hashtable 的定义

public class Hashtable<K,V>
    extends Dictionary<K,V>
    implements Map<K,V>, Cloneable, java.io.Serializable

该子类的保存数据和Map类一致 ;

注:目前将方法contains()改为以下两种:–containsValue() 值判断
--------------------------------------------------------containsKey() 键判断;

1.3.4 TreeMap 子类
?概述

TreeSet是一个有序集合,可以以任意顺序将元素插入到集合中,在对集合进行遍历的时候,每个元素将自动按照排序后的顺序呈现。底层使用的是二叉树(更具体点是红黑树)实现,对于元素之间排序,如果不指定自定义的比较器Comparator,那么插入的对象 必须实现Comparable 接口,元素按照实现此接口的compareTo()方法去排序。如果指定了自定义的比较器Comparator,优先使用Comparator去对元素进行排序。比较规则决定了元素是否可以重复,以及元素的排序结果。

** ?数据结构**:红黑树

? TreeMap 特性

(1)保存的元素按照一定的规则有序
(2)可自定义排序规则
(3)非线程安全

底层分析:和TreeSet的相类似;

?TreeMap 排序方法

public class MapTest{
    public static void main(String[] srgs){
    //one =1,two =2;
    Map<String,Integer> map = new TreeMap<>();//k,v型
    map.put("A",1);
    map.put("C",3);
    map.put("B",2);
    System.out.printf(map);
    //结果:
    //{A=1,B =2,C= 3}//自然排序默认
	}
}

自然排序和定制排序和TreeSet一致(因为TreeSet保存的值为Map的键Key储存的值)利用Key值来进行排序;

?hashCode()与equals()的相关规定:

如果两个对象相等,则hashcode一定也是相同的 两个对象相等,对两个equals方法返回true
两个对象有相同的hashcode值,它们也不一定是相等的 综上,equals方法被覆盖过,则hashCode方法也必须被覆盖
hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

1.3.5 Map.Entry 内部接口
?概述

在Map类设计是,提供了一个嵌套接口(static修饰的接口):Entry。Entry将键值对的对应关系封装成了对象,即键值对对象,这样我们在遍历Map集合时,就可以从每一个键值对(Entry)对象中获取对应的键与对应的值。可以获取相应的值;

范例

public class MapEntryTest{
	public static void main(String[] args){
		Map.Entry<String,Integer> entry= Map.entry("one",1);
		System.out.printf(entry.getKey());//获取Key
		System.out.printf(entry.getValue());//获取Value
	}
}

2.1 Iterator 迭代输出

?概述

Java集合框架的集合类,我们有时候称之为容器。容器的种类有很多种,比如ArrayList、LinkedList、HashSet…,每种容器都有自己的特点,ArrayList底层维护的是一个数组;LinkedList是链表结构的;HashSet依赖的是哈希表,每种容器都有自己特有的数据结构。
  因为容器的内部结构不同,很多时候可能不知道该怎样去遍历一个容器中的元素。所以为了使对容器内元素的操作更为简单,Java引入了迭代器模式!

?Iterator中常用方法

public boolean hasNext();//判断下一个是否有值
public E next();//先移动到下一位置取出元素;
public default void remove();//移除当前元素

?代码实现

public class Demo1 {
	public static void main(String[] args) {
		Collection coll = new ArrayList();
		//List<String> list = new ArrayList();//List和Set都需要先获取iterator方法
		/*Set<String> all = Set.of("1","2","3");
		Iterator<String> iter = all.iterator();
		*/
		coll.add("aaa");
		coll.add("bbb");
		coll.add("ccc");
		coll.add("ddd");
		System.out.println(coll);
		Iterator it = coll.iterator();
		while (it.hasNext()) {
			it.next();
			it.remove();
		}
		System.out.println(coll);
	}
}

注意:

1.next()方法和remove()连用:使用remove()必须使用next();
2.迭代出来的元素只是迭代器copy了原集合的元素或者对象,并没有对元素类进行改变,只是引用;

?怎么使用迭代器?

Iterator 的特点是只能单向遍历,但是更加安全,因为它可以确保,在当前遍历的集合元素被更改的时候,就会抛出 ConcurrentModificationException 异常。

;原文链接:https://blog.csdn.net/weixin_50999696/article/details/115410914
本站部分内容转载于网络,版权归原作者所有,转载之目的在于传播更多优秀技术内容,如有侵权请联系QQ/微信:153890879删除,谢谢!

推荐图文


随机推荐