0
点赞
收藏
分享

微信扫一扫

【JavaSE基础】06-集合框架(一)

吓死我了_1799 2022-04-29 阅读 42
java集合

文章目录

引入

假设存在学生类 Student ,现在需要对学生进行分班操作,根据之前的学习内容,需要先使用学生信息创建学生对象,然后存储在数组中(目前,作为容器的 JAVA 对象只有两类:数组和字符串缓冲区)。

// 入学时,软件A1班级,有5名同学
Student s1 = new Student("201501","Apple");
Student s2 = new Student("201502","Merry");
Student s3 = new Student("201503","Davoid");
Student s4 = new Student("201504","Max");
Student s5 = new Student("201505","Joian");

// 由需求创建一个 Student 数组,长度为5
Student[] classA1 = new Strudent[] { s1, s2, s3, s4, s5 };

// 第二学期,因Merry学业成绩不及格,被留级
// 将Merry所在的数组中位置使用 null 取代
classA1[1] = null;

// 第三学期,因低年级的 Yzk 学业成绩优秀,跳级到A1班级
// 很简单嘛,Yzk 占了 Merry 的位置就好了呀
String s6 = new Student("201301","Yzk");
classA1[1] = s6;

// 但是,高年级的 Dou 也被留级到A1班,怎么办呢?

/**
 * 假设开学的时候,班级一共100人,中间成绩不好的留级了25人,
 * 建立的100人的数组,是不是太过于浪费内存呢? */
/**  
 * 假设开学的时候,班级一共100人,高年级留级、低年级跳级的人一共有35人,
 * 空间不够又怎么办呢? */

上面的例子中,追根究底,一切源于数组的不可变性,数组在创建时,或显式或隐式的确定了数组长度,并且不可修改。数组长度的不可变性,不能适应变化的需求,就像是 String 的不可变性,给频繁的拼接操作带来了额外的内存消耗,Java提供了 StringBuilder、StringBuffer这一对 内容可变的、适应于单线程高效和多线程安全的类,解决了 String 的不可变性问题。那么,Java如何解决的数组的不可变性问题呢?

Java是纯面向对象的语言,而面向对象语言对事物的描述是通过对象体现的,为了方便的对多个对象进行操作,我们就必须对多个对象进行存储,而欲存储对象则需一容器\集合,目前所学中,仅有 数组Array[] 和 字符串缓冲区为此类,然字符串缓冲区输出为字符串,故不可用。再者 数组Array[] 需用对象数组,而其却不适应变化的需求,故弃之不用。鉴于此,Java提供了集合类解决这一问题。

示例:验证集合中只可以保存引用数据类型

/** 原始代码 */
import java.util.ArrayList;
import java.util.Collection;

public class Demo4
{
	public static void main(String[] args)
	{
		Collection c = new ArrayList();
		c.add(1);
		c.add(1.0);
		c.add(true);
		c.add('1');
	}
}

/** 反编译代码 */
import java.util.ArrayList;
import java.util.Collection;

public class Demo4
{
  public static void main(String[] args)
  {
    Collection c = new ArrayList();
    c.add(Integer.valueOf(1));
    c.add(Double.valueOf(1.0D));
    c.add(Boolean.valueOf(true));
    c.add(Character.valueOf('1'));
  }
}

集合是存储多个元的,但存储多元,也有不同的需求,譬如:多个元不可重复、多个元有序的。针对不同的要求,Java提供了不同的集合类,多个集合类的数据结构不同,并且还能使用的存储的东西,例如:判断、获取

集合之间有共性的内容,将共性的内容向上抽取,最后形成了现在继承体系。

在这里插入图片描述

Collection 接口

Collection 接口是集合类层次结构的根接口

1、集合可以理解为动态的对象数组,不同的是集合中的对象内容可以任意扩充。
2、集合的特点:高性能、容易扩展和修改。没有直接实现的具体类,有实现集合的接口,子接口有具体实现类。
3、Collection 常用的子接口:List、Set、Queue、Deque
4、Collection 的功能概述

  • a、添加功能

    • boolean add(E o)
      如果此 collection 由于调用而发生更改,则返回 true。如果此 collection 不允许有重复元素,并且已经包含了指定的元素,则返回 false
    • boolean addAll(Collection<? extends E> c)
      如果此 collection 由于调用而发生更改,则返回 true
  • b、判断功能(相等、为空、包含)

    • boolean contains(Object obj)
      当且仅当此 collection 至少包含一个满足 (obj==null ? e==null : obj.equals(e)) 的元素 e 时,返回 true
    • boolean containsAll(Collection<?> c)
      如果此 collection 包含指定 collection 中的所有元素,则返回 true
    • boolean isEmpty()
      如果此 collection 不包含元素,则返回true
    • boolean boolean equals(Object o)
      比较此 collection 与指定对象是否相等。
  • c、删除功能(交集、差集等)

    • void clear()
      移除此 collection 中的所有元素(可选操作)。此方法返回后,除非抛出一个异常。
    • boolean remove(Object o)
      如果此 collection 包含一个或多个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则移除这样的元素。如果此调用将移除一个元素,则返回 true
    • boolean removeAll(Collection<?> c)
      移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。此调用返回后,collection 中将不包含任何与指定 collection 相同的元素。 如果此 collection 由于调用而发生更改,则返回 true
    • boolean retainAll(Collection<?> c)
      仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。换句话说,移除此 collection 中未包含在指定 collection 中的所有元素。 如果此 collection 由于调用而发生更改,则返回 true
      注意:最终结果存入当前的Collection,c不变
  • d、获取功能(遍历和长度获取)

    • Iterator<E> iterator()
      返回在此 collection 的元素上进行迭代的迭代器。关于元素返回的顺序没有任何保证(除非此 collection 是某个能提供保证顺序的类实例)。
    • int size()
      返回此collection中的元素个数
  • e、转换功能(集合转换为数组)

    • Object[] toArray()
      返回包含此 collection 中所有元素的数组
    • <T> T[] toArray(T[] a)
      如果此 collection 对其迭代器返回的元素顺序做出了某些保证,那么此方法必须以相同的顺序返回这些元素。
      像 toArray() 方法一样,此方法充当基于数组的 API 与基于 collection 的 API 之间的桥梁。更进一步说,此方法允许对输出数组的运行时类型进行精确控制,并且在某些情况下,可以用来节省分配开销。
      假定 x 是只包含字符串的一个已知 collection。以下代码用来将 collection 转储到一个新分配的 String 数组: String[] y = x.toArray(new String[0]); 注意: toArray(new Object[0])toArray() 在功能上是相同的。

示例1:基本功能测试
示例2:高级功能(All)测试

// 最简单的代码,没有之一! 自己写 !!!!!!

集合遍历

1、使用方法 Object[] toArray() 转换为数组,再进行便利
2、使用方法 Iterator<E> iterator() 集合专用的迭代器遍历

首先,来看一下,迭代器相关得信息。

迭代器 (Iterator)

public interface Iterator<E> 集合专用遍历方式

对 Collection 进行迭代的迭代器。迭代器取代了Java Collections Framework 中的 Enumeration 。迭代器与枚举有两点不同:

  • 迭代器允许调用者利用定义好的语法在迭代期间从迭代器所指向的 Collection 中移除元素
  • 方法名称得到了改进

成员方法【Method Summary】

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

示例1:遍历字符串的集合

package com.rupeng.collection;

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

public class Demo2
{
	public static void main(String[] args)
	{
		Collection<String> c = new ArrayList<String>();
		c.add("I");
		c.add("Love");
		c.add("Java");
		c.add("2015");
		c.add("07");
		c.add("05");
		c.add("RuPeng.com");
		// 1、遍历方式1 toArray()
		Object[] objs = c.toArray();
		for (int i = 0; i < objs.length; i++)
		{
			System.out.print(objs[i] + " ");
		}
		System.out.println();
		System.out.println("---------------------------------");
		// 2、遍历方式2 iterator()
		// Iterator<String> it = c.iterator();
		// while (it.hasNext())
		// {
		// System.out.print(it.next() + " ");
		// }
		for (Iterator<String> it = c.iterator(); it.hasNext();)
		{
			String str = it.next();
			System.out.print(str + " ");
		}
	}
}

示例2:遍历自定义对象的集合

package com.rupeng.collection;

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

public class Demo3
{
	public static void main(String[] args)
	{
		Student s1 = new Student("Merry", 12);
		Student s2 = new Student("IAn", 18);
		Student s3 = new Student("Mix", 15);

		Collection<Student> c = new ArrayList<Student>();
		c.add(s1);
		c.add(s2);
		c.add(s3);

		Object[] objs = c.toArray();
		for (int i = 0; i < objs.length; i++)
		{
			Student s = (Student) objs[i];
			System.out.println(s);
		}

		System.out.println("------------------------------------");

		// Iterator<Student> it = c.iterator();
		// while (it.hasNext())
		// {
		// Student s = (Student) it.next();
		// System.out.println(s);
		// }
		for (Iterator<Student> it = c.iterator(); it.hasNext();)
		{
			Student stu = (Student) it.next();
			System.out.println(stu);
		}
	}
}
// 获取迭代器实例
Iterator it = c.iterator();  
// 判断  it.hasnext()
// 指针  it.next()

源码

public interface Iterable<T>
{
	Iterator<T> iterator();  // 仅有方法
	// 默认修饰符:public abstract
}

public interface Iterator<E>
{
	/** 所有方法 */
	boolean hasNext();
	E next();
	void remove();
	// 默认修饰符:public abstract
}

public interface Collection<E> implements Iterable<E>
{
	Iterator<E> iterator();  // 一个方法,只是继承没有给出实现
}

public interface List<E> extends Collection<E>
{
		Iterator<E> iterator();  // 一个方法,只是继承没有给出实现
}

public class ArrayList<E> implements List<E>
{
	public Iterator<E> iterator() 
	{
		return new Itr();
    }
	/** 私有成员内部类(安全) */
	private class Itr implements Iterator<E>
	{
		public boolean hasNext() { // 功能代码快 }
		public E next() { // 功能代码快 }
		public void remove() { // 功能代码快 }
	}
}

List 接口

public interface List<E> extends Collection<E>

有序的集合,也称为序列、列表。可以对列表中的每个元素的插入位置进行精确控制,可以根据元素的索引访问、搜索元素。

与set不同,列表通常允许重复的元素。更确切地讲,列表通常允许存在满足 e1.equals(e2)的元素对e1、e2,允许多个null元素 。

List 接口提供了 4 种对列表元素进行定位(索引)的访问方法。列表(像 Java 数组一样)是基于 0 的。注意:这些操作可能在执行时间上与某些实现类的索引值成比(例如:LinkedList),因此在不知道具体实现类时,在列表上使用迭代通常优于用索引遍历列表。

List 接口为其具体实现类提供了针对于列表的迭代器 ListIterator,除了继承 Iterator 的正常操作外,还允许元素的插入和替换,以及双向访问。还提供了重写的方法实现从列表中指定位置开始遍历的列表迭代器。

List 接口提供了两种搜索指定对象的方法。从性能上来看,很多的实现中,是一种高开销的线性搜索。

List 接口提供了两种在列表的任意位置高效插入和移除多个元素的方法。

1:功能概述

除了继承了所有 Collection 接口的方法外,还提供了列表关于索引的一系列操作。

  • a、添加功能

    • boolean add(E o)
    • boolean add(int index,E o) (list 定义)
      在指定索引位置 index 插入元素。如果此 collection 由于调用而发生更改,则返回 true。如果此 collection 不允许有重复元素,并且已经包含了指定的元素,则返回 false
    • boolean addAll(Collection<? extends E> c)
    • boolean addAll(int index,Collection<? extends E> c) (list 定义)
      在指定索引位置,插入集合c中的所有元素。的如果此 collection 由于调用而发生更改,则返回 true
  • b、判断功能(相等、为空、包含)

    • boolean contains(E o)
      当且仅当此 collection 至少包含一个满足 (obj==null ? e==null : obj.equals(e)) 的元素 e 时,返回 true
    • boolean containsAll(Collection<?> c)
      如果此 collection 包含指定 collection 中的所有元素,则返回 true
    • boolean isEmpty()
      如果此 collection 不包含元素,则返回true
    • boolean equals(Object o)
      比较此 collection 与指定对象是否相等。
  • c、删除功能(交集、差集等)

    • E remove(int index) (list 定义)
      删除指定索引处的元素。将所有的后续元素向左移动(将其索引减 1)。返回从列表中移除的元素。
    • boolean remove(Object o)
      如果此 collection 包含一个或多个满足 (o==null ? e==null : o.equals(e)) 的元素 e,则移除这样的元素。如果此调用将移除一个元素,则返回 true
    • boolean removeAll(Collection<?> c)
      移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。此调用返回后,collection 中将不包含任何与指定 collection 相同的元素。 如果此 collection 由于调用而发生更改,则返回 true
    • boolean retainAll(Collection<?> c)
      仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。换句话说,移除此 collection 中未包含在指定 collection 中的所有元素。 如果此 collection 由于调用而发生更改,则返回 true
      注意:最终结果存入当前的Collection,c不变
    • void clear()
      移除此 collection 中的所有元素(可选操作)。此方法返回后,除非抛出一个异常。
  • d、获取功能(遍历和长度获取)

    • int indexOf(Object obj) (list 定义)
      返回元素第一次出现的索引位置。返回满足 (o==null ? get(i)==null : o.equals(get(i))) 的最低索引 i;如果没有这样的索引,则返回 -1。
    • int lastIndexOf(Object obj) (list 定义)
      返回元素最后一次出现的索引位置。返回满足 (o==null ? get(i)==null : o.equals(get(i))) 的高索引 i;如果没有这样的索引,则返回 -1。

List 接口提供了两种搜索指定对象的方法。从性能上来看,很多的实现中,是一种高开销的线性搜索

  • d、获取功能(遍历和长度获取)

    • ListIterator<E> listIterator() (list 定义)
      返回此列表元素的列表迭代器(按适当顺序)。

    • ListIterator<E> listIterator(int index) (list 定义)
      返回此列表元素的列表迭代器(按适当顺序)。从列表的指定位置开始。指定的索引表示 next 的初始调用所返回的第一个元素。 previous 方法的初始调用将返回的索引比指定索引少 1 的元素。

    • List<E> subList(int fromIndex, int toIndex) (list 定义)
      1、 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。(如果 fromIndex 和 toIndex 相等,则返回的子列表为空,但是针对子列表的操作,会在原列表的 fromIndex 体现)。返回的子列表由原列表支持,因此子列表中的非结构性更改将反映在原列表中,反之亦然。返回的子列表支持原列表支持的所有可选列表操作。
      2、此方法省去了显式范围操作(此操作通常针对数组存在)。通过传递 subList 视图而非整个列表,期望列表的任何操作可用作范围操作。例如,下面的语句从列表中移除了指定范围的元素list.subList(from, to).clear(); 可以对 indexOf 和 lastIndexOf 构造类似的语句,而且 Collections 类中的所有算法都可以应用于 subList。

    • Iterator<E> iterator()
      返回在此 collection 的元素上进行迭代的迭代器。关于元素返回的顺序没有任何保证(除非此 collection 是某个能提供保证顺序的类实例)。

    • int size()
      返回此collection中的元素个数

  • e、转换功能(集合转换为数组)

    • Object[] toArray()
      返回包含此 collection 中所有元素的数组
    • <T> T[] toArray(T[] a)
      如果此 collection 对其迭代器返回的元素顺序做出了某些保证,那么此方法必须以相同的顺序返回这些元素。
      像 toArray() 方法一样,此方法充当基于数组的 API 与基于 collection 的 API 之间的桥梁。更进一步说,此方法允许对输出数组的运行时类型进行精确控制,并且在某些情况下,可以用来节省分配开销。
      假定 x 是只包含字符串的一个已知 collection。以下代码用来将 collection 转储到一个新分配的 String 数组: String[] y = x.toArray(new String[0]); 注意: toArray(new Object[0])toArray() 在功能上是相同的。
  • f、列表的读写器(Setter \ Getter)(List 定义)

    • E get(int index)
      返回列表中指定位置的元素。

    • E set(int index, E element)
      用指定元素替换列表中指定位置的元素。 返回的是替换之前的值。

2、列表四种遍历方式

1、转换为对象数组 + for 循环

Object[] arrs = list.toArray();
for (int i = 0; i < arrs.length; i++)
{
	String str = (String) arrs[i];
	System.out.print(str + " ");
}

2、集合的遍历 iterator + for 、while

for (Iterator<String> it = list.iterator(); it.hasNext();)
{
	String str = it.next();
	System.out.print(str + " ");
}

/**---------------------------------*/

Iterator<String> it = list.iterator();
while (it.hasNext())
{
	String str = it.next();
	System.out.print(str + " ");
}

3、列表的遍历 listIterator + for、while

for (Iterator<String> iterator = list.listIterator(); iterator
		.hasNext();)
{
	String str = iterator.next();
	System.out.print(str + " ");
}

/**---------------------------------*/

Iterator<String> iterator = list.listIterator();
while (iterator.hasNext())
{
	String str = iterator.next();
	System.out.print(str + " ");
}

4、Setter \ Getter + for 循环

for (int i = 0; i < list.size(); i++)
{
	String str = list.get(i);
	System.out.print(str + " ");
}

3、迭代器 (ListIterator)

public interface ListIterator<E> extends Iterator<E>

列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。ListIterator 没有当前元素;它的光标位置 始终位于调用 previous() 所返回的元素和调用 next() 所返回的元素之间。

在这里插入图片描述

注意:remove() 和 set(Object) 方法不是 根据光标位置定义的;它们是根据对调用 next() 或 previous() 所返回的最后一个元素的操作定义的。
逆向遍历:要实现逆向遍历,必须先正向遍历,将指针索引移动,故此,逆向遍历无意义

主要方法如下

  • 继承自 Iterator 的三个主要方法:向后遍历 + 移除

    • 判断 : boolean hasNext() 是否存在下一个元素
    • 获取 : E next() 指针后的元素
    • 移除 : void remove() 移除 next\previous 获取的元素
  • ListIterator 特有的功能:逆向遍历

    • 判断 : boolean hasPrevious() 是否存在前一个元素
    • 获取 : E previous() 指针前的元素
    • 索引 : int previousIndex() 指针前的元素索引
    • 索引 : int nextIndex() 指针后的元素的索引 ,值等于 previousIndex()+1
  • ListIterator 特有的功能:修改值

    • 添加 : void add(E e) 指针位置之后 插入 元素 e
    • 设置 : void set(E e) 设置 获取的元素

示例:遍历字符串列表,如果存在元素“world”的话,就插入“JAVASE”

package com.rupeng.collection;

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

public class ListDemo1
{
	public static void main(String[] args)
	{
		// 创建并初始化 字符串列表
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");
		list.add("java");

		// 使用 for 亦可
		Iterator<String> it = list.listIterator();
		while (it.hasNext())
		{
			String str = it.next();
			if (str == "world")
			{
				list.add("JAVASE");
			}
		}
		System.out.println(list);
	}
}

说明:异常分析
1、现象:
java.util.ConcurrentModificationException 并发修改异常。当检测到对象不可以并法修改时,但进行了,抛出此异常。快速失败

2、原因:
当使用迭代器遍历时,不可通过集合修改元素(安全性)。也就是说,迭代器遍历元素时,集合修改元素,迭代器不可获知,也是产生了此异常

3、解决方案:
针对 List 集合 和 它特有的迭代器 ListIterator ,主要分为两种解决方式:

  • 使用 List 集合的方法遍历修改【示例1:集合遍历元素,集合修改元素】;
  • 通过 ListIterator 迭代器的修改值功能【示例2:迭代器遍历元素,迭代器修改元素】

示例1:List 集合方法重构

package com.rupeng.collection;

import java.util.ArrayList;
import java.util.List;

public class ListDemo2
{
	public static void main(String[] args)
	{
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");
		list.add("java");
		/** 集合遍历 */
		for (int i = 0; i < list.size(); i++)
		{
			String str = list.get(i);
			if (str == "world")
			{
				/** 集合修改 */
				/** 集合的添加功能默认是添加在集合的尾部 */
				/** list.add("JAVASE"); */
				list.add(i + 1, "JAVASE");
			}
		}
		System.out.println(list);
	}
}

示例2:ListIterator 迭代方法重构

package com.rupeng.collection;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListDemo2
{
	public static void main(String[] args)
	{
		List<String> list = new ArrayList<String>();
		list.add("hello");
		list.add("world");
		list.add("java");
		/** 迭代器遍历 */
		ListIterator<String> it = list.listIterator();
		while (it.hasNext())
		{
			String str = it.next();
			if (str == "world")
			{
				/** 迭代器修改 */
				it.add("JAVASE");
			}
		}
		System.out.println(list);
	}
}

4、常用的数据结构

A:栈【Stack】

First In,Last Out

在这里插入图片描述

B:队列【Queue】

First in, First Out

在这里插入图片描述

C:数组【Array】

定义:存储同一种数据类型的多个元素容器、集合。需要连续的内存空间。有索引,方便操作。

从示例中,体会数组的优缺点:

int[] arr = {11, 22, 33, 44, 55};

1、插入:在元素33之后插入66

  • 新建一个比原始数组长度大 1 的数组 int[] newArr = new int[arr.length+1];
  • 遍历原始数组 33 (含)之前的元素,之后插入66,然后继续遍历原始数组

2、删除:删除元素 33

  • 新建一个比原始数组长度小 1 的数组 int[] newArr = new int[arr.length-1];
  • 遍历原始数组 33 之前的元素,遇到 33 跳过(continue),然后继续遍历原始数组

3、查询:获取元素 33 所在索引;获取具体索引位置元素

  • 使用 for 循环遍历、比较元素
  • 可以使用优化较好的算法进行查找,譬如:Arrays.binarySearch(value) 二分查找

D:链表【LinkedList】

定义:多个类型的相同的元素通过链式存储方式组成的有序序列。不需要连续的内存空间。结构:每一个元素由数据域和指针域组成,数据域存储元素自身信息,指针域存储后继结点的位置。

在这里插入图片描述

从示例中,体会链表的优缺点:

1、插入:在元素 33 之后插入 66

  • 创建元素 66 的存储(数据域为 66,指针域为 空)
  • 遍历找到元素 33 (因链表结构,姑需遍历找)
  • 将元素 33 的指针域赋给 元素 66 的指针域
  • 将元素 33 的指针域修改为 元素 66 的地址

在这里插入图片描述

2、删除:删除元素 33

  • 遍历找到元素 33 及其前一项
  • 修改元素 33 的前项的指针域,改为 元素 33 的指针域内容。亦即将元素33 前一项直接指向 元素 33 的后项,将 元素 33 从链表中删除了。

在这里插入图片描述
3、查询:获取元素 33 所在索引;获取具体索引位置元素

  • 从头遍历,对比指针域内容即可。

面试题


LinkedList 因为继承了Deque 双向队列,所有有一些 双向队列的特有功能

  • 1、向集合首尾添加元素:void addFirst(E e)void addLast(E e)
  • 2、获取集合首尾的元素:E getFirst()E getLast()
  • 3、删除集合首尾元素:E removeFirst()E removeLast()

其中有一些功能可以用来构造其他的数据结构。

/** MyStack 数据结构 */
package com.rupeng.collection;

import java.util.LinkedList;
import java.util.ListIterator;

public class MyStack<E>
{
	private LinkedList<E> list;

	public MyStack()
	{
		list = new LinkedList<E>();
	}

	public boolean empty()
	{
		if (list.isEmpty())
		{
			return true;
		}
		return false;
	}

	public E peek()
	{
		return list.getLast();
	}

	public E pop()
	{
		/** 返回并删除尾元素 */
		return list.removeLast();
	}

	public E push(E item)
	{
		list.addLast(item);
		return list.getLast();
	}

	@SuppressWarnings("unchecked")
	public int search(Object o)
	{
		return search0((E) o);
	}

	private int search0(E e)
	{
		int index = -1;
		ListIterator<E> it = list.listIterator();
		while (it.hasNext())
		{
			E e2 = it.next();
			if (e2 == e)
			{
				index = it.previousIndex();
			}
		}
		return index;
	}

	@Override
	public String toString()
	{
		return list.toString();
	}
}
/** 测试自定义 MyStack 数据结构 */
package com.rupeng.collection;

public class MyStackDemo
{
	public static void main(String[] args)
	{
		MyStack<String> ms = new MyStack<String>();
		ms.push("Rupeng");
		ms.push("Yzk");
		ms.push("JoianSUN");
		ms.push("JAVA");
		System.out.println(ms);
		ms.peek();
		System.out.println(ms);
		ms.pop();
		System.out.println(ms);
		ms.push("20150705");
		System.out.println(ms);
		int index = ms.search("Yzk");
		System.out.println(index);
		System.out.println(ms.empty());
	}
}



实现方式1:使用额外的集合进行去重复 【空间换取时间、时间复杂度O(n)】

package com.rupeng.collection;

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

/** 使用另一个 List 集合,遍历时执行相同值的过滤 */
public class RepeatStringDemo1
{
	public static void main(String[] args)
	{
		List<String> list = new ArrayList<String>();
		list.add("WWW");
		list.add("Rupeng");
		list.add("COM");
		list.add("WWW");
		list.add("Rupeng");
		list.add("JAVA");
		System.out.println(list);

		List<String> anotherList = new ArrayList<String>();
		for (Iterator<String> it = list.iterator(); it.hasNext();)
		{
			String str = it.next();
			if (!anotherList.contains(str))
			{
				anotherList.add(str);
			}
		}
		System.out.println(anotherList);
	}
}

实现方式2:使用集合自身进行去重复 【时间换取空间、时间复杂度O(n2)】

/** 使用集合自身进行去重复 */
package com.rupeng.collection;

import java.util.ArrayList;
import java.util.List;

/**
 * 使用自身进行值得过滤:
 * 
 * 方法:第一个元素和第二个(含)以后所有的元素进行对比,重复就移除;
 * 		第二个和第三个(含)元素之后得所有元素对比,重复就移除;
 * 		以此类推,直到倒数第二个元素比较之后,就结束
 */
public class RepeatStringDemo2
{
	public static void main(String[] args)
	{
		List<String> list = new ArrayList<String>();
		list.add("WWW");
		list.add("WWW");
		list.add("Rupeng");
		list.add("Rupeng");
		list.add("COM");
		list.add("JAVA");
		list.add("Rupeng");
		list.add("Rupeng");
		list.add("JAVA");
		System.out.println(list);

		for (int i = 0; i < list.size() - 1; i++)
		{
			String str1 = list.get(i);
			for (int j = i + 1; j < list.size(); j++)
			{
				String str2 = list.get(j);
				if (str1 == str2)
				{
					list.remove(j);
					/**
					 * 因为ArrayList自身长度的自增自减,
					 * 导致连续相同的元素会出现两次,
					 * 所以,对于同一个位置,需要存在相同元素再遍历以次 
					 */
					j--;
				}
			}
		}
		System.out.println(list);
	}
}

实现方式3:借助 Set 集合的唯一性、LinkedHashSet 的有序且唯一性实现去重复

package com.rupeng.practice;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/*
 *  3、使用 Set 集合的唯一性特点实现;
 *     存在一个缺点:会使得去重复之后的集合变得无序
 *     但是可以使用 LinkedHashSet 实现,避免这个问题!
 */
public class ListDemo1
{
	public static void main(String[] args)
	{
		List<String> list = new ArrayList<String>();
		list.add("Hello");
		list.add("Java");
		list.add("Rupeng");
		list.add("JavaSE");
		list.add("Java");
		list.add("Hello");
		list.add("Rupeng");
		/** 3、使用 Set 集合的唯一性特点实现 */
		// (1)定义一个 LinkedHashSet 实现去重复
		Set<String> set = new LinkedHashSet<String>();
		// (2)遍历原始集合,将元素逐个添加到 set 中
		for (String str : list)
		{
			set.add(str);
		}
		// (3)使用继承自 AbstractCollection的toString 打印集合
		System.out.println("3、使用 Set 集合的唯一性特点实现 */");
		System.out.println(set);
		System.out.println("--------------------------------");
	}
}

实现方式1:使用额外的集合去重 【空间换取时间、时间复杂度O(n)】

package com.rupeng.collection;

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

public class RepeatStudentDemo1
{
	public static void main(String[] args)
	{
		Student s1 = new Student("Joian", 12);
		Student s2 = new Student("Joian", 12);
		Student s3 = new Student("Joian", 12);
		Student s4 = new Student("Dou", 15);
		Student s5 = new Student("Dou", 15);
		Student s6 = new Student("Yzk", 18);

		List<Student> list = new ArrayList<Student>();
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
		list.add(s6);
		System.out.println(list);

		List<Student> anotherList = new ArrayList<Student>();
		Iterator<Student> it = list.iterator();
		while (it.hasNext())
		{
			Student stu = it.next();
			if (!anotherList.contains(stu))
			{
				anotherList.add(stu);
			}
		}
		System.out.println(anotherList);
	}
}

查看 contains 方法的源码

/** ArrayList 源代码 */
public class ArrayList<E>
{
	public boolean contains(Object o)
	{
		return indexOf(o) >= 0;
	}

	public int indexOf(Object o)
	{
		if (o == null)
		{
			for (int i = 0; i < size; i++)
			{
				if (elementData[i] == null)
				{
					return i;
				}
			}
		} else
		{
			for (int i = 0; i < size; i++)
			{
				if (o.equals(elementData[i]))
				{
					return i;
				}
			}
			return -1;
		}
	}
}

从源码中可以看出来。实际上 contains 方法的底层实现依赖于 equals ,但是自定义对象 Student 中没有重写来自 Object 类的 equals 方法,导致进行 contains 方法的判断时,比较的是 元素的地址值。而 String 类重写了来自 Object 类 的 equals,所以没有此类问题。

实现方式2:使用集合自身去重 【时间换取空间、时间复杂度O(n2)】

package com.rupeng.collection;

import java.util.ArrayList;
import java.util.List;

public class RepeatStudentDemo2
{
	public static void main(String[] args)
	{
		Student s1 = new Student("Joian", 12);
		Student s2 = new Student("Joian", 12);
		Student s3 = new Student("Joian", 12);
		Student s4 = new Student("Dou", 15);
		Student s5 = new Student("Dou", 15);
		Student s6 = new Student("Yzk", 18);

		List<Student> list = new ArrayList<Student>();
		list.add(s1);
		list.add(s2);
		list.add(s3);
		list.add(s4);
		list.add(s5);
		list.add(s6);
		System.out.println(list);

		for (int i = 0; i < list.size() - 1; i++)
		{
			Student stu1 = list.get(i);
			for (int j = i + 1; j < list.size(); j++)
			{
				Student stu2 = list.get(j);
				if (stu1.equals(stu2))
				{
					list.remove(j);
					j--;
				}
			}
		}
		System.out.println(list);
	}
}
举报

相关推荐

0 条评论