首页 > 其他分享 >常用对象API(集合框架Collection)★★★★★

常用对象API(集合框架Collection)★★★★★

时间:2023-02-02 20:35:14浏览次数:61  
标签:元素 Collection Person add API 集合 new public

目录

概述

image-20221028165644115

image-20221024124855388

image-20221024200406024

数组也可以存对象,但是数组长度是固定的,使用集合长度是可变的,不确定长度用集合好一些

注意:集合里面是保存的对象

集合类的由来:

对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定。

就使用集合容器进行存储。

集合的特点:

  1. 对象封装数据,对象多了也需要存储。集合用于存储对象的容器。

  2. 对象的个数确定可以使用数组,但是不确定怎么办?可以用集合。因为集合是可变长度的。

  3. 集合中不可以存储基本数据类型值。

集合和数组的区别:

  1. 数组是固定长度的;

​ 集合可变长度的。

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

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

  1. 数组存储的元素必须是同一个数据类型;

​ 集合存储的对象可以是不同数据类型。

体系&共性功能

数据结构:就是容器中存储数据的方式。

集合容器因为内部的数据结构不同,有多种具体容器。

集合容器在不断的向上抽取,就形成了集合框架。

在使用一个体系时,原则:参阅顶层内容。建立底层对象。

框架的顶层就是Collection接口:

image-20221024145909895

Collection接口的常见方法:

  1. 添加。
boolean add(Object obj)://添加元素。

boolean addAll(Collection coll)://添加某集合所有元素
//创建一个集合容器。使用Collection接口的子类。ArrayList
ArrayList c1 = new ArrayList();

//1,添加元素。
	c1.add("abc1");//add(Object obj);
	c1.add("abc2");
	c1.add("abc3");
	c1.add("abc4");
打印:["abc1","abc2"....]
ArrayList c2 = new ArrayList();
	c2.add("abc5");//add(Object obj);
	c2.add("abc6");
	c2.add("abc7");
c1.addAll(c2); //将c2中的元素添加到c1中

image-20221024172555898

  1. 删除。
boolean remove(object obj):

boolean removeAll(Collection coll);
//将两个集合中的相同元素从调用removeAll的集合中删除。

void clear();//清空集合。
 //演示removeAll

boolean b =c1.removeAll(c2);
//将两个集合中的相同元素从调用removeAll的集合中删除。

System.out.println("removeAll:"+b);

image-20221024173257336

  1. 判断:
boolean contains(object obj):
//如果此集合包合此元素,返回true。

boolean containsAll(Colllection coll);
//如果此集合包含集合所有元素,返回true。

boolean isEmpty();//判断集合中是否有元素。
//演示containsAll

boolean b =c1.containsAll(c2);
//如果此集合包含集合所有元素,返回true。

System.out.println("containsAll:"+b); 
//输出:containsAll:false

image-20221024175634983

  1. 获取:
int size();//返回此集合中的元素数。
// 如果此集合包含Integer.MAX_VALUE元素,则返回Integer.MAX_VALUE 。 
  1. 取交集:
boolean retainAll(Collection coll);//取交集。


  //演示retainAll

boolean b = c1.retainAll(c2);
//取交集,保留和指定的集合相同的元素,而删除不同的元素。
//和removeAll功能相反。

System.out.println("retainAll:"+b);

image-20221024175504903

  1. 获取集合中所有元素:
Iterator iterator();//取出元素的方式:迭代器。

/*该对象必须依赖于具体容器,因为每一个容器的数据结构都不同。

所以该迭代器对象是在容器中进行内部实现的。

对于使用容器者而言,具体的实现不重要,只要通过容器获取到该实现的迭代器的对象即可,

也就是iterator方法。
    
Iterator接口就是对所有的Collection容器进行元素取出的公共接口。

其实就是抓娃娃游戏机中的夹子!
*/

7.将集合变成数组

Object[] toArray()://将集合转成数组。

迭代器的使用Iterator

Interface Iterator

Iterator是一个接口。作用:用于取集合中的元素。

boolean [hasNext] 如果仍有元素可以迭代,则返回 true。
E [next] 返回迭代的下一个元素。
void [remove]从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
Collection coll = new ArrayList();

	coll.add("abc1");

    coll.add("abc2");

    coll.add("abc3");

    coll.add("abc4");
//      System.out.println(coll);
    //使用了Collection中的iterator()方法。 调用集合中的迭代器方法,是为了获取集合中的迭代器对象。
//      Iterator it =coll.iterator();     

//      while(it.hasNext()){

//          System.out.println(it.next());

//      }
    for(Iterator it = coll.iterator(); it.hasNext(); ){
        System.out.println(it.next());

    }
//实际开发中用这种,for循环释放内存
//但是如果你想循环结束以后迭代器还能用就使用while,for与while的区别不就是在这儿吗。

迭代器原理:

  • 取元素,无论是什么数据结构,最终共性的取出方式:
    一个一个取,取之前判断。有,取一个。没有,结束
  • 这种共性的方式称为:迭代。

img

image-20221024203333996

List和Set的特点

List(列表)和set(集)

Collection

|--List:有序(存入和取出的顺序一致),该集合体系元素都有索引(角标),元素可以重复

|--Set:元素不能重复,无序(LinkedHashSet集合有序)。

所以有序不有序不重要,重要的是唯不唯一,需要唯一走Set,不需要走List

使用集合的一些技巧

需要唯一吗?

  • 需要:用Set
    • 需要制定顺序:
      • 需要TreeSet
      • 不需要HashSet
      • 但是想要一个和存储一致的顺序(有序):LinkeHashSet
  • 不需要List
    • 需要频繁增删吗?
      • 需要:LinkeList
      • 不需要:ArrayList

如何记录每个容器的结构和所属体系呢?

List

  • ArrayList
  • LinkedList

Set

  • HashSet
  • TreeSet

后缀名就是该集合所属的体系

前缀名就是该集合的数据结构

  • 看到array:就要像到数组,就要想到查询快,有角标。
  • 看到Link:就要想到链表,就要想到增删快,就要想到add,get,remove+frist、last方法。
  • 看到hash:就要想到哈希表,就要想到唯一性,就要想到元素需要覆盖hashCode方法和equals方法。
  • 看到tree:就要想到二叉树,就要想到排序,就要想到两个接口Comparable,Comparator。

List接口

List接口的常见方法

List特有的常见方法:有一个共性方法就是操作角标

 List本身是Collection接口的子接口,具备了Collection的所有方法。现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引,这是该集合最大的特点。

 List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

List常用子类的特点:

  • Vector:底层的数据结构就是数组,线程同步的,被ArrayList替代了,Vector无论查询和增删都巨慢。
  • ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快,但是增删稍慢。
  • LinkedList:底层的数据结构是链表,线程不同步增删元素的速度非常快,查询稍慢

可变长度数组的原理:

    当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。

    ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。

    Vector:是按照原数组的100%延长。

  注意对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。所以建议元素都要复写equals方法,建立元素对象自己的比较相同的条件依据。

说说ArrayList为什么查询快,为什么LinkList为什么查询慢的原因吗?(ps按ctrl键跳转

  List集合支持对元素的增、删、改、查。

  1. 添加
void add(int index, E element) 在此列表中的指定位置插入指定的元素。
boolean add(E e) 将指定的元素追加到此列表的末尾。

    add(index,element) :在指定的索引位插入元素。

    addAll(index,collection) :在指定的索引位插入一堆元素。

  1. 删除

    remove(index) :删除指定索引位的元素。 返回被删的元素。

  1. 获取

    Object get(index) :通过索引获取指定元素。

    int indexOf(obj) :获取指定元素第一次出现的索引位,如果该元素不存在返回—1;

     所以,通过—1,可以判断一个元素是否存在。

    int lastIndexOf(Object o) :反向索引指定元素的位置。

    List subList(start,end) :获取子列表。包含头不包含尾。

  1. 修改

    Object set(index,element) :对指定索引位进行元素的修改。返回被修改的元素

  1. 获取所有元素

    ListIterator listIterator():list集合特有的迭代器。

 List集合因为角标有了自己的获取元素的方式: 遍历。

for(int x=0; x<list.size(); x++){
   
	sop("get:"+list.get(x));
}

  在进行list列表元素迭代的时候,如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素。会发生.ConcurrentModificationException并发修改异常。

  导致的原因是:

    集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。

  如何解决呢?

    既然是在迭代中对元素进行操作,找迭代器的方法最为合适.可是Iterator中只有hasNext,next,remove方法.通过查阅的它的子接口,ListIterator,发现该列表迭代器接口具备了对元素的增、删、改、查的动作。

代码实现:

import java.util.*;
class ListDemo 
{
   public static void sop(Object obj)
   {
      System.out.println(obj);
   }
   public static void method()
   {
      
      ArrayList al = new ArrayList();

      //添加元素
      al.add("java01");
      al.add("java02");
      al.add("java03");
      
      sop("原集合是:"+al);
      //在指定位置添加元素。
      al.add(1,"java09");

      //删除指定位置的元素。
      //al.remove(2);

      //修改元素。
      //al.set(2,"java007");

      //通过角标获取元素。
      sop("get(1):"+al.get(1));

      sop(al);

      //获取所有元素。
      for(int x=0; x<al.size(); x++)
      {
         System.out.println("al("+x+")="+al.get(x));
      }

      Iterator it = al.iterator();

      while(it.hasNext())
      {
         sop("next:"+it.next());
      }


      //通过indexOf获取对象的位置。
      sop("index="+al.indexOf("java02"));

      List sub = al.subList(1,3);

      sop("sub="+sub);
   }

ListIterator接口

ListIterator是List集合特有的迭代器。ListIterator是Iterator的子接口。

ListIterator it = list.listIterator;//取代Iterator it = list.iterator;

在迭代时,不可以通过集合对象的方法操作集合中的元素。
因为会发生ConcurrentModificationException异常。

所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,
只能对元素进行判断,取出,删除的操作,
如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator

该接口只能通过List集合的listIterator方法获取。

    • | Modifier and Type | 方法 | 描述 |
      | ----------------- | ----------------- | ------------------------------------------------------------ |
      | void | add(E e) | 将指定的元素插入列表(可选操作)。 |
      | boolean | hasNext() | 如果此列表迭代器在向前方向遍历列表时具有更多元素,则返回 true 。 |
      | boolean | hasPrevious() | 如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回 true 。 |
      | E | next() | 返回列表中的下一个元素,并且前进光标位置。 |
      | int | nextIndex() | 返回由后续调用返回的元素的索引 next() 。 |
      | E | previous() | 返回列表中的上一个元素,并向后移动光标位置。 |
      | int | previousIndex() | 返回由后续调用返回的元素的索引 previous() 。 |
      | void | remove() | 从列表中删除 next()previous() (可选操作)返回的最后一个元素。 |
      | void | set(E e) | 用指定的元素(可选操作)替换 next()previous()返回的最后一个元素。 |
public static void sop(Object obj)
	{
		System.out.println(obj);
	}
public static void main(String[] args) 
{

   //演示列表迭代器。
   ArrayList al = new ArrayList();

   //添加元素
   al.add("java01");
   al.add("java02");
   al.add("java03");

   sop(al);

   
   ListIterator li = al.listIterator();

   
   //sop("hasPrevious():"+li.hasPrevious());
    
	//正向遍历
   while(li.hasNext())
   {
      Object obj = li.next();

      if(obj.equals("java02"))
         //li.add("java009");
         li.set("java006");

   }

	//逆向遍历
   while(li.hasPrevious())
   {
      sop("pre::"+li.previous());
   }
   //sop("hasNext():"+li.hasNext());
   //sop("hasPrevious():"+li.hasPrevious());
    
   sop(al);



   /*
   //在迭代过程中,准备添加或者删除元素。

   Iterator it = al.iterator();

   while(it.hasNext())
   {
      Object obj = it.next();

      if(obj.equals("java02"))
         //al.add("java008");
         //java.util.ConcurrentModificationException 故障快速迭代器异常
         一个线程通常不允许修改集合,而另一个线程正在遍历它。
         it.remove();//将java02的引用从集合中删除了。

      sop("obj="+obj);


   }
   sop(al);
   */
   


}

ConcurrentModificationException:

image-20221025140052285

数组和链表

image-20221025153144385

为什么快:因为空间是连续的所以快

为什么慢:空间不连续吗,挨个去判断吗

LinkedList有角标吗?

先不说原因,它是不是list接口的子类,是啊,list最大的特点是不是有角标啊,所以也它有啊

Vector集合(了解)

只是讲解了Vector迭代的特有方法而已,但是我们现在一般都不用了,只是作为了解而已

该接口的功能由Iterator接口复制。 此外, Iterator添加了可选的删除操作,并且具有较短的方法名称。 新的实现应该考虑使用Iterator ,而不是Enumeration 。 因此能够适应一个Enumeration到Iterator通过使用asIterator()方法。

/*
枚举就是Vector特有的取出方式。
发现枚举和迭代器很像。
其实枚举和迭代是一样的。

因为枚举的名称以及方法的名称都过长。
所以被迭代器取代了。
枚举郁郁而终了。


*/
class VectorDemo 
{
	public static void main(String[] args) 
	{
		Vector v = new Vector();

		v.add("java01");
		v.add("java02");
		v.add("java03");
		v.add("java04");

		Enumeration en = v.elements();

		while(en.hasMoreElements())
		{
			System.out.println(en.nextElement());
		}
        
        //用迭代器代替
        Iterator it =v.iterator();

        while(it.hasNext()){
            System.out.println("next:"+it.next());
        }
	}
}

       
    }

LinkedList集合

void addFirst(); //在该列表开头插入指定的元素。 

void addLast(); //将指定的元素追加到此列表的末尾。 

//JDK1.6后
boolean offerFirst();//在此列表的前面插入指定的元素。 

boolean offerLast(); //在该列表的末尾插入指定的元素。 

返回null的作用是可以加判断

e getFirst();//获取链表中的第一个元素。如果链表为空,抛出NoSuchElementException;

e getLast();

//在jdk1.6以后。返回null的作用是可以加判断

E peekFirst();//获取链表中的第一个元素。如果链表为空,返回null。

E peekLast();

E removeFirst()://获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,抛出NoSuchElementException

E removeLast();

//在jdk1.6以后。

E pollFirst();//获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,返回null。

E pollLast();
  LinkedList link =new LinkedList();

        link.addFirst("abc1");

        link.addFirst("abc2");

        link.addFirst("abc3");

        link.addFirst("abc4");

//      System.out.println(link);

//      System.out.println(link.getFirst());//获取第一个但不删除。

//      System.out.println(link.getFirst());

//      System.out.println(link.removeFirst());//获取元素但是会删除。

//      System.out.println(link.removeFirst());

        while(!link.isEmpty()){
            System.out.println(link.removeLast());

        }

        System.out.println(link);

//      Iterator it =link.iterator();

//      while(it.hasNext()){

//          System.out.println(it.next());

//      }

    }

练习#(堆栈和队列)

★★★面试考点:

面讲一个在面试中经常会被出的面试题

/*

 * 请使用LinkedList来模拟一个堆栈或者队列数据结构。
 

 * 堆栈:先进后出 First In Last Out  FILO


 * 队列:先进先出 First In First Out FIFO


 * 我们应该描述这样一个容器,给使用提供一个容器对象完成这两种结构中的一种。

 */

并不是需要你把这样一个效果演示完,而是需要你做一个这样的容器出来,
是我要给你提供一个对象,这个对象就能实现,你怎么存进去,你调用我的方法时候就能怎么取出来,你得去把这种结构封装起来

 

这里面的代码就不贴出来了,先自己去做一下,

 

LinkedList:

 

    addFirst();

    addLast():

    jdk1.6

    offerFirst();//在此列表的开头插入指定的元素

    offetLast();

   

   

    getFirst();.//获取但不移除,如果链表为空,抛出NoSuchElementException.

    getLast();

    jdk1.6

    peekFirst();//获取但不移除,如果链表为空,返回null.

    peekLast():

   

    removeFirst();//获取并移除,如果链表为空,抛出NoSuchElementException.

    removeLast();

    jdk1.6

    pollFirst();//获取并移除,如果链表为空,返回null.

    pollLast();

ArrayList集合存储自定对象

定义一个ArrayList,然后往这里面存入Person,然后再把这里面的数据遍历出来

import java.util.*;

/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。

比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。


思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。



List集合判断元素是否相同,依据是元素的equals方法。

*/

class Person
{
	private String name;
	private int age;
	Person(String name,int age)
	{
		this.name = name;
		this.age = age;
	}
	
	public boolean equals(Object obj)
	{

		if(!(obj instanceof Person))
			return false;

		Person p = (Person)obj;
		//System.out.println(this.name+"....."+p.name);

		return this.name.equals(p.name) && this.age == p.age;
	}
	/**/
	public String getName()
	{
		return name;
	}
	public int getAge()
	{
		return age;
	}
}
class ArrayListTest2 
{
	public static void sop(Object obj)
	{
		System.out.println(obj);
	}
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();

		al.add(new Demo());

		al.add(new Person("lisi01",30));//al.add(Object obj);//Object obj = new Person("lisi01",30);
		//al.add(new Person("lisi02",32));
		al.add(new Person("lisi02",32));
		al.add(new Person("lisi04",35));
		al.add(new Person("lisi03",33));
		//al.add(new Person("lisi04",35));

		
		//al = singleElement(al);

		sop("remove 03 :"+al.remove(new Person("lisi03",33)));//remove方法底层也是依赖于元素的equals方法。


		Iterator it = al.iterator();


		while(it.hasNext())
		{
			Person p = (Person)it.next();
			sop(p.getName()+"::"+p.getAge());
		}
	}


	public static ArrayList singleElement(ArrayList al)
	{
		//定义一个临时容器。
		ArrayList newAl = new ArrayList();

		Iterator it = al.iterator();

		while(it.hasNext())
		{
			Object obj = it.next();

			if(!newAl.contains(obj))
				newAl.add(obj);

		}

		return newAl;
	}
}

**堆内存图: **PS:集合中装的全是对象的引用,不可能直接把对象装进去。

image-20221028170618535

Set接口

Set接口特点

Set:元素不可以重复,是无序。

Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。

  • HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效;

    • HashSet是如何保证元素唯一性的呢?

      • 是通过元素的两个方法,hashCode和equals来完成。
      • 如果元素的hashcode值不同,不会调用equals,就直接存储到哈希表中。
      • 如果元素的HashCode值相同,才会判断equals是否为true。
      • 如果为true,视为相同元素,不存。如果为false,那么视为不同元素,就进行存储。
    • 注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。

    • 一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals,hashCode方法。

      建立对象判断是否相同的依据。

  • LinkedHashSet:有序,hashSet的子类,此线程不同步。

  • TreeSet:对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。

    • ​ 判断元素唯一性的方式,就是根据比较方法的返回结果是否为0;是0,就是相同元素。

    • ​ TreeSet对元素进行排序的方式一:

      ​ 让元素自身具备比较功能,元素就需要实现Comperable接口。覆盖compareTo方法。

HashSet集合

    • | Modifier and Type | 方法 | 描述 |
      | ----------------- | -------------------- | ------------------------------------------------------------ |
      | boolean | add(E e) | 将指定的元素添加到此集合(如果尚未存在)。 |
      | void | clear() | 从此集合中删除所有元素。 |
      | Object | clone() | 返回此 HashSet实例的浅拷贝:元素本身不被克隆。 |
      | boolean | contains(Object o) | 如果此集合包含指定的元素,则返回 true 。 |
      | boolean | isEmpty() | 如果此集合不包含元素,则返回 true 。 |
      | Iterator<E> | iterator() | 返回此集合中元素的迭代器。 |
      | boolean | remove(Object o) | 如果存在,则从该集合中删除指定的元素。 |
      | int | size() | 返回此集合中的元素数(其基数)。 |
      | Spliterator<E> | spliterator() | 在此集合中的元素上创建late-binding故障快速 Spliterator 。 |

image-20221029115015758

package com.practice;


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

public class Demo {

    public static void sop(Object obj)
    {
        System.out.println(obj);
    }
    public static void main(String[] args)
    {

        HashSet hs = new HashSet();

        sop(hs.add("java01"));
        sop(hs.add("java01"));
        hs.add("abc1");
        hs.add("abc2");
        hs.add("abc3");
        hs.add("abc4");

        Iterator it = hs.iterator();

        while(it.hasNext())
        {
            sop(it.next());
        }
    }
}

哈希表

在使用HashSet在存储元素的时候也,有一个hash算法

image-20221029151845989

hashCode判断元素相同的方式

image-20221029151639549

哈希表的原理:

    1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。

    2,哈希值就是这个元素的位置。

    3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

    4,存储哈希值的结构,我们称为哈希表。

    5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。

      这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

哈希表确定元素是否相同

是通过元素的两个方法,hashCodeequals来完成。

  1. 判断的是两个元素的哈希值是否相同

    • 如果元素的HashCode值相同,才会判断equals是否为true。
  2. 判断哈希值相同,其实判断的是对象的hashCode的方法。

    • 判断内容相同,用的是equals方法。

      • 如果为true,视为相同元素,不存。
      • 如果为false,那么视为不同元素,就进行存储。
    • 注意:如果哈希值不同,是不需要判断equals。

一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都覆盖equals,hashCode方法。

建立对象判断是否相同的依据:

  • 对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

  • 对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

HashSet存储自定义对象

package com.practice;

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

public class Demo {

    public static void sop(Object obj)
    {
        System.out.println(obj);
    }

    public static void main(String[] args)
    {
        HashSet hs = new HashSet();
        /*
         * HashSet集合数据结构是哈希表,所以存储元素的时候
         * 使用的元素的hashCode方法来确定位置,如果位置相同,在通过元素的equals来确定是否相同。
         *
         * */
        hs.add(new Person("shishuo01",18));
        hs.add(new Person("shishuo02",19));
        hs.add(new Person("shishuo03",19));
        hs.add(new Person("shishuo03",19));
        hs.add(new Person("shishuo04",20));


//        sop("a1:"+hs.contains(new Person("shishuo04",20)));//如果此集合包含指定的元素,则返回 true

//		hs.remove(new Person("shishuo04",20));//移除


        Iterator it = hs.iterator();

        while(it.hasNext())
        {
            Person p = (Person)it.next(); //add()方法的参数是Object,所以这里要做类型提升
            sop(p.getName()+"::"+p.getAge());
        }
    }
}

class Person
{
    private String name;
    private int age;
    Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    @Override
    public int hashCode()
    {
//        System.out.println(this.name+"....hashCode");
        return name.hashCode()+age*37;
        //age乘一个数字,减少哈希冲突,然后去调用equals,
        //尽量保持哈希值唯一,提升效率
    }
    @Override
    public boolean equals(Object obj)
    {
        //健壮性判断
		if(this == obj){
            return true;
        }
        if(!(obj instanceof Person))//instanceof可以用来判断继承中的子类的实例是否为父类的实现
            throw new ClassCastException("类型错误");

        Person p = (Person)obj;
//        System.out.println(this.name+"...equals.."+p.name);

        return this.name.equals(p.name) && this.age == p.age;
    }


    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
}

练习(消除ArrayList中重复元素 )

package com.Set;

import java.util.ArrayList;
import java.util.Iterator;

/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。

比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。


思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。

List集合判断元素是否相同,依据是元素的equals方法。


*/
public class LinkedHashSet {

    public static void main(String[] args)
    {
        ArrayList arrayList = new ArrayList();

        arrayList.add(new Person("shishuo01",18));
        arrayList.add(new Person("shishuo02",19));
        arrayList.add(new Person("shishuo03",19));
        arrayList.add(new Person("shishuo03",19));
        arrayList.add(new Person("shishuo03",19));
        arrayList.add(new Person("shishuo03",19));
        arrayList.add(new Person("shishuo04",20));



        System.out.println(arrayList);
        sop("a1:"+arrayList.contains(new Person("shishuo04",20)));//如果此集合包含指定的元素,则返回 true

        arrayList.remove(new Person("shishuo04",20));//移除

        arrayList = getSingleElement(arrayList);


        System.out.println(arrayList);

    }


    public static ArrayList getSingleElement(ArrayList arrayList) {

        //1.定义一个临时容器
        ArrayList temp = new ArrayList();

        //2.迭代元素
        Iterator iterator = arrayList.iterator();

        while (iterator.hasNext()){

            Object obj = iterator.next();
			//3.判断被迭代的元素是否存在于临时容器中
            if (!temp.contains(obj))//因为contains方法也是用equals的基础来判断的,所以要重写equals方法		
                temp.add(obj);

        }
        return temp;
    }

    private static void sop(String s) {
        System.out.println(s);
    }
}

class Person
{
    private String name;
    private int age;
    Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
//    @Override
//    public int hashCode()
//    {
////        System.out.println(this.name+"....hashCode");
//        return name.hashCode()+age*37;
//        //age乘一个数字,减少哈希冲突,然后去调用equals,
//        //尽量保持哈希值唯一,提升效率
//    }

    //对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

    @Override
    public boolean equals(Object obj)
    {
        //健壮性判断
        if(this == obj){
            return true;
        }
        if(!(obj instanceof Person))//instanceof可以用来判断继承中的子类的实例是否为父类的实现
            throw new ClassCastException("类型错误");

        Person p = (Person)obj;
//        System.out.println(this.name+"...equals.."+p.name);

        return this.name.equals(p.name) && this.age == p.age;
    }


    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    public String toString(){
        return name+":"+age;
    }
}

LinkedHashSet集合

image-20221030111321070

所以有序不有序不重要,重要的是唯不唯一,需要唯一走Set,不需要走List


HashSet hs = new LinkedHashSet();
//多态指向子类的add

hs.add("java02");
hs.add("java03");
hs.add("java03");
hs.add("java04");

Iterator it = hs.iterator();

while(it.hasNext())
{
	System.out.println(it.next());
}

TreeSet集合

TreeSet概述特点

TreeSet:可以对Set集合中的元素的指定顺序排序。是不同步的。排序需要依据元素自身具备的比较性。

如果元素不具备比较性,在运行时会发生ClassCastException异常。

所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法

依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。

TreeSet方法保证元素唯一性的方式:就是参考比较方法的返回值结果是否为0,如果return 0,视为两个对象重复,不存。

 

注意

在进行比较时,如果判断元素不唯一,比如,同姓名,同年龄,才视为同一个人。

在判断时,需要分主要条件和次要条件,当主要条件相同时,再判断次要条件,按照次要条件排序。

TreeSet集合排序两种方式

Comparable和Comparator区别:

1. 让元素自身具备比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。

  • 如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。也就是说对象是别人的,你无法进去覆盖compareTo,只能当个舔狗,适应人家的对象,怎么办?

2. 让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。

第二种方式较为灵活,Comparator优先。

package com.Set;

import java.util.Comparator;
import java.util.Iterator;
import java.util.TreeSet;
/*
*按Person_01类中年龄排序
*
 */


public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new ComparatorByName());

        treeSet.add(new Person_0("shishuo_01",18));
        treeSet.add(new Person_0("shishuo_02",18));
        treeSet.add(new Person_0("chenyufan",18));
        treeSet.add(new Person_0("shiying",20));
        treeSet.add(new Person_0("chaiyun",21));
        treeSet.add(new Person_0("cwhat",22));

        Iterator iterator = treeSet.iterator();

        while (iterator.hasNext()){
            Person_0 p = (Person_0)iterator.next();
            System.out.println("姓名:"+p.getName()+"  年龄:"+p.getAge());
        }
    }
}
class Person_0 implements Comparable
{
    private String name;
    private int age;
    Person_0(String name,int age)
    {
        this.name = name;
        this.age = age;
    }
    //方法一:按Person_01类中年龄排序,元素对象实现Comparable接口,覆盖compareTo方法
    @Override
    public int compareTo(Object o) {
        Person_0 p = (Person_0) o;

        int temp = this.age - p.age;
        return temp==0?this.name.compareTo(p.name):temp; //漂亮代码

/*      烂透了代码
        if (this.age > p.age)
            return 1;
        if (this.age < p.age)
            return -1;
        else
            return this.name.compareTo(p.name);
 */
    }

    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    public String toString(){
        return name+":"+age;
    }
}
//第二种方法实现Comparator接口的比较器
class ComparatorByName implements Comparator
{
    public int compare(Object o1,Object o2)
    {
        Person_0 s1 = (Person_0)o1;
        Person_0 s2 = (Person_0)o2;

        int temp = s1.getName().compareTo(s2.getName());
        return temp == 0 ?s1.getAge()-s2.getAge():temp;

       // return 1;//有序
       // return -1;//倒序

    }
}
/*
姓名:chaiyun  年龄:21
姓名:chenyufan  年龄:18
姓名:cwhat  年龄:22
姓名:shishuo_01  年龄:18
姓名:shishuo_02  年龄:18
姓名:shiying  年龄:20

TreeSet集合——二叉树

image-20221030195031857

小插曲:

怎样实现怎么存进去就怎样取出来(有序)

Comparator接口比较器的compare返回值为1,就可以,怎样比较都是1,结果为正数,就会有序输出。

反之返回值改为-1,就是倒序。优雅,实在是太优雅了

TreeSet练习

/*
* 对字符串进行长度排序
*/
public class TreeSetDemo {
    public static void main(String[] args) {
        TreeSet treeSet = new TreeSet(new CompareByLength());

        treeSet.add("shishuo_01");
        treeSet.add("shiying");
        treeSet.add("shishuo_02");
        treeSet.add("chenyufan");
        treeSet.add("chaiyun");
        treeSet.add("cwhat");

        Iterator iterator = treeSet.iterator();

        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}
class CompareByLength implements Comparator {

    @Override
    public int compare(Object o1, Object o2) {
        String s1 = (String) o1;
        String s2 = (String) o2;

        int temp = s1.length() - s2.length();
        return temp == 0 ? s1.compareTo(s2) : temp;
    }
}
/*
输出:
cwhat
chaiyun
shiying
chenyufan
shishuo_01
shishuo_02

泛型

概述

泛型:JDK1.5 出现的安全机制。表现格式:<>

好处:

  1. 将运行时期的问题ClassCastException()转到了编译时期。
  2. 避免了强制转换的麻烦。

<>:什么时候用?

当操作的引用数据类型不确定的时候。就使用<>。将要操作的引用数据类型转入即可。

其实<>就是一个用于接收具体引用数据类型的参数范围。

在程序中,只要用到了带有<>的类或者接口,就要明确传入的具体引用数据类型。

当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。

小知识:Class ArrayList 这个E就是引用数据类型的意思,就当是Element(元素),有具体数据类型的话就被强制转换为传入的具体数据类型。

擦除&补偿

泛型技术其实应用在编译时期,是给编译器使用的技术,到了运行时期,泛型就不存在了。

  为什么? 因为泛型的擦除

也就是说,编辑器检查了泛型的类型正确后,在生成的类文件中是没有泛型的。

 

在运行时,如何知道获取的元素类型而不用强转呢?

 泛型的补偿:因为存储的时候,类型已经确定了是同一个类型的元素,所以在运行时,只要获取到该元素的类型,在内部进行一次转换即可,所以使用者不用再做转换动作了。

import java.util.*;
class GenericDemo2 
{
   public static void main(String[] args) 
   {
      TreeSet<String> ts = new TreeSet<String>(new LenComparator());

      ts.add("abcd");
      ts.add("cc");
      ts.add("cba");
      ts.add("aaa");
      ts.add("z");
      ts.add("hahaha");


      Iterator<String> it = ts.iterator();

      while(it.hasNext())
      {
         String s = it.next();
         System.out.println(s);
          //补偿:Class c = "abcd".getClass();获取到该元素的类型
          //		c.getName(); 元素类型名称。
      }
   }
}


class LenComparator implements Comparator<String>
{
   public int compare(String o1,String o2)
   {
      int num = new Integer(o2.length()).compareTo(new Integer(o1.length()));

      if(num==0)
         return o2.compareTo(o1);
      return num;
   }
}

泛型类

什么时候用泛型类呢?

当类中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。

class Worker{}
class Student{}
//泛型前做法。
class Tool
{
	private Object obj;
	public void setObject(Object obj)
	{
		this.obj = obj;
	}
	public Object getObject()
	{
		return obj;
	}
}

//泛型类。
//在jdk1.5后,使用泛型来接收类中要操作的引用数据类型。
/*
什么时候定义泛型类?
当类中要操作的引用数据类型不确定的时候,
就用泛型类来表示。
*/
class Utils<QQ>
{
	private QQ q;
	public void setObject(QQ q)
	{
		this.q = q;
	}
	public QQ getObject()
	{
		return q;
	}
}

class  GenericDemo3
{
	public static void main(String[] args) 
	{

		Utils<Worker> u = new Utils<Worker>();

		u.setObject(new Worker());
		Worker w = u.getObject();;
		/*
		Tool t = new Tool();
		t.setObject(new Student());
		Worker w = (Worker)t.getObject();
		*/
	}
}

泛型方法

/**
     * 
     * 将泛型定义在方法上。
     */

    public <W> void show(W str){

        System.out.println("show : "+str.toString());

    }

    public void print(QQ str){

        System.out.println("print : "+str);

    }

    /**

     * 当方法静态时,不能访问类上定义的泛型。如果静态方法使用泛型,

     * 只能将泛型定义在方法上。

     * @param obj

     */
//静态方法上的泛型:静态方法无法访问类上定义的泛型。
//如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
    public static <Y> void method(Y obj){

        System.out.println("method:"+obj);

    }

    Tool<String> tool = new Tool<String>();

       

        tool.show(new Integer(4));

        tool.show("abc");

        tool.print("hahah");

//      tool.print(newInteger(8));

        Tool.method("haha");

        Tool.method(new Integer(9));

泛型接口

package com.Generic;

public class GenericDemo_2 {

    public static void main(String[] args) {
        InterImp interImp = new InterImp();
        interImp.show("陈宇凡");

        InterImp_02<Integer> imp_02 = new InterImp_02<Integer>();//在调用的时候才明确数据类型
        imp_02.show(1228);
        
    }
}


interface Inter<T>{public void show(T t);}
//第一种开始就确定了数据类型
class InterImp implements Inter<String>{

    @Override
    public void show(String s) {
        System.out.println(s);
    }
}
//第二种,不明确数据类型,将问题抛给调用者
class InterImp_02<W> implements Inter<W>{
    @Override
    public void show(W w) {
        System.out.println(w);
    }
}

泛型限定 ——上下限

泛型中的通配符:可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。

  泛型限定:

    上限:?extends E:可以接收E类型或者E的子类型对象。

    下限:?super E:可以接收E类型或者E的父类型对象。

 private static void PrintCollection(Collection<? extends Person> al)
 private static void PrintCollection(Collection<? super Student> al)

  上限什么时候用:往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。为什么?因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。

  下限什么时候用:当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。

泛型的细节:

  1. 泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;

  2. 使用带泛型的类创建对象时,等式两边指定的泛型必须一致;

    原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了;

  3. 等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容);

    ArrayList<String> al = new ArrayList<Object>(); //错
    

    //要保证左右两边的泛型具体类型一致就可以了,这样不容易出错。

    ArrayList<? extends Object> al = new ArrayList<String>();
    
    al.add("aa"); //错
    
    

    因为集合具体对象中既可存储String,也可以存储Object的其他子类,所以添加具体的类型对象不合适,类型检查会出现安全问题。

    ?extends Object 代表Object的子类型不确定,怎么能添加具体类型的对象呢?

    public static void method(ArrayList<? extends Object> al) {
    
    	al.add("abc"); //错
    
    }
    

    只能对al集合中的元素调用Object类中的方法,具体子类型的方法都不能用,因为子类型不确定。

package com.Generic;
/*
* 上下限演示:
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class GenericAdvanceDemo {

    public static void main(String[] args) {

        ArrayList<Person> arrayList = new ArrayList<Person>();
        arrayList.add(new Person("石硕",12));
        arrayList.add(new Person("陈宇凡",13));

        ArrayList<Student_> shi = new ArrayList<Student_>();
        shi.add(new Student_("shishuo",14));
        shi.add(new Student_("chenyufan",15));

        ArrayList<String> shuo = new ArrayList<String>();
        shuo.add("wflafl");
        shuo.add("fhsalfla");

        PrintCollection(arrayList);
        PrintCollection(shi);
//        PrintCollection(shuo);

    }
    private static void PrintCollection(Collection<? extends Person> al) {
                                        //Collection<Person> al =new ArrayList<Person>();

        Iterator<? extends Person> it = al.iterator();
        while (it.hasNext()){
//            T t = it.next();
//            System.out.println(it.next());
            Person p = it.next();
            System.out.println(p);
        }

    }
//    private static<T> void PrintCollection(Collection<T> al) {
//
//        Iterator<T> it = al.iterator();
//        while (it.hasNext()){
//            T t = it.next();
//            System.out.println(t);
//        }
//    }
}
class Person
{
    private String name;
    private int age;
    Person(String name,int age)
    {
        this.name = name;
        this.age = age;
    }

    public String getName()
    {
        return name;
    }
    public int getAge()
    {
        return age;
    }
    public String toString(){
        return name+":"+age;
    }
}
class Student_ extends Person{

    Student_(String name, int age) {
        super(name, age);
    }
}

泛型限定——上限的体现

   public static void main(String[] args) {
       

        ArrayList<Person>al1 = new ArrayList<Person>();

        al1.add(new Person("abc",30));

        al1.add(new Person("abc4",34));
       

        ArrayList<Student>al2 = new ArrayList<Student>();

        al2.add(new Student("stu1",11));

        al2.add(new Student("stu2",22));
       

        ArrayList<Worker>al3 = new ArrayList<Worker>();

        al3.add(new Worker("stu1",11));

        al3.add(new Worker("stu2",22));

       
        ArrayList<String>al4 = new ArrayList<String>();

        al4.add("abcdeef");

//      al1.addAll(al4);//错误,类型不匹配。

        al1.addAll(al2);

        al1.addAll(al3);

        System.out.println(al1.size());


//      printCollection(al2);

//      printCollection(al);
    }
}


/*
 * 一般在存储元素的时候都是用上限,因为这样取出都是按照上限类型来运算的。不会出现类型安全隐患。
 */

class MyCollection<E>{
    public void add(E e){

    }

    public void addAll(MyCollection<? extends E> e){

    }
}

泛型限定——下限的体现

    public static void main(String[] args) {


        TreeSet<Person> al1 = new TreeSet<Person>(new CompByName());

        al1.add(new Person("abc4",34));

        al1.add(new Person("abc1",30));

        al1.add(new Person("abc2",38));


        
        TreeSet<Student>al2 = new TreeSet<Student>(new CompByName());

        al2.add(new Student("stu1",11));

        al2.add(new Student("stu7",20));

        al2.add(new Student("stu2",22));



        TreeSet<Worker>al3 = new TreeSet<Worker>();

        al3.add(new Worker("stu1",11));

        al3.add(new Worker("stu2",22));


        TreeSet<String>al4 = new TreeSet<String>();

        al4.add("abcdeef");
        

//      al1.addAll(al4);//错误,类型不匹配。



//      al1.addAll(al2);

//      al1.addAll(al3);


//      System.out.println(al1.size());

        Iterator<Student>it = al2.iterator();

        while(it.hasNext()){
            System.out.println(it.next());

        }

    }

}

/*

 * class TreeSet<Worker>

 * {

 *     Tree(Comparator<? super Worker> comp);

 * }


 * 什么时候用下限呢?通常对集合中的元素进行取出操作时,可以是用下限。


 */


class CompByName implements Comparator<Person>{

    @Override

    public int compare(Person o1, Person o2) {

        int temp = o1.getName().compareTo(o2.getName());

        return temp==0? o1.getAge()-o2.getAge():temp;
    }

}

class CompByStuName implements Comparator<Student>{

    @Override

    public int compare(Student o1, Student o2) {

        int temp = o1.getName().compareTo(o2.getName());

        return temp==0? o1.getAge()-o2.getAge():temp;

    }

}


class CompByWorkerName implements Comparator<Worker> {

    @Override

    public int compare(Worker o1, Worker o2) {

        int temp = o1.getName().compareTo(o2.getName());

        return temp==0? o1.getAge()-o2.getAge():temp;

    }
}

标签:元素,Collection,Person,add,API,集合,new,public
From: https://www.cnblogs.com/Nuff5a1d/p/17087304.html

相关文章

  • 常用对象API(集合框架)★★★★★
    目录目录目录集合框架Collection概述体系&共性功能Collection接口的常见方法:迭代器的使用IteratorList和Set的特点使用集合的一些技巧List接口List接口的常见方法ListIter......
  • 其他对象API
    System类API———java.lang.System:属性和行为都是静态的。longcurrentTimeMillis();//返回当前时间毫秒值//作用演示longl1=1335664696656l;//System.curr......
  • 面试题之List集合如何实现去重?
    关于List集合去重的问题其实是很简单的不过简单的问题要尽量考虑全面一些!要考虑JDK1.8的新特性实现List集合去重的三种方式:1、方式一直接定义一个方法循环遍历判断......
  • 常用对象API(String类)
    目录StringBuffer字符串缓冲区特点&添加功能增删改查和可变数组长度StringBuilder类StringBuilder练习:String类String类特点:构造函数字符串常见方法获取转换判断比较inter......
  • stream操作常用API 示例详解
    简介从JDK8开始,增加了一新特性Stream流式操作,Stream中提供了非常多的API供大家使用,灵活的使用这些API,可以非常的方便且优美的实现我们的代码逻辑。最终型toArraytoArray......
  • cache API简介
    cacheAPI是一个很强大的API,它可以在window环境和serviceWorker环境使用,配合serviceWorker可以让我们自己来管理缓存。cachescaches是一个全局接口,可以在window和worker......
  • 常用API大全分享!赶紧收藏起来!
    一、短信发送短信的应用可以说是非常的广泛了,短信API也是当下非常热门的API~短信验证码:可用于登录、注册、找回密码、支付认证等等应用场景。支持三大运营商,3秒可达,99.9......
  • VUE API接口的统一管理
    vue项目对api进行封装统一管理在日常vue项目中或刚接收一个vue项目,如果项目足够大,就需要进行统一管理和规范接口了1.js文件创建先在src目录下新建一个api文件夹,然后......
  • 使用python编写简单的api接口
    先安装flask模块pipinstallFlaskpy文件fromflaskimportFlask,request,jsonifyapp=Flask(__name__)@app.route('/hello_world')defhello_world():r......
  • Yapi 部署 (Docker-compose部署)
    目录资源清单一、Docker安装1.使用国内yum源2.卸载旧版本的docker3.安装Docker20.10版本4.设置镜像加速5.启动docker二、Docker-compose安装1.Docker-compos......