黑马程序员----牛叉的集合之List

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

集合

1.java集合体系


Collection接口
--List接口:元素是有序的,且可以存放重复数据,因为该体系有下标,该体系具备修改功能,修改指定位置的元素。
--ArrayList
--LinkedList
--Vector
特有方法:
凡是可以操作下标的方法都是该体系特有的方法
1.add(index,element);addAll(index,collection)。
2.remove(index)。
3.indexOf(element);get(index);listInterator();subList(from,to)。
4.set(index,element)。
--Set接口:元素是无序的,不可以存放重复数据
--HashSet
--TreeSet

每个容器对对象的存储方式不同,也就是数据结构不同。
集合中存放的并不是对象实体,而是存放对象的地址
集合中有一个可变长度的int[],接收任意类型的对象地址,之所以能接收任意类型,也是因为不管什么类型,地址都是int型。
直接打印一个集合,会得到[e1,e2,e3,e4]格式的输出。

2.集合的用处

当我们需要存储多个不同类型且数量不定的对象就需要使用集合体系中的


3.集合类与数组

a.集合类只能存储对象,数组还可以存储基本数据类型。
b.集合类长度是可变的,而数组是固定的。
c.数组中存储的数据一次只能是一个类型。
d.集合可以同时存储多个类型的对象。

4.List集合常用方法

1.增加
add,addAll(注意:此处的addAll将一个集合的内容添加到当前集合中,但是不是挨个添加,而是作为一个对象,整个添加进来,我们无法匹配其中个别内容,只能匹配整个对象)
2.删除
remove,removeAll,clear,retainAll(求交集)
注意:删除时,有两个重载remove(int index),remove(Object obj)如果传入一个int型,那么不管这个int型是否在合理范围内,都会调用remove(int index)方法,因此可能会报下标越界错误
3.转换
toArray
4.获取
iterator,size
iterator返回一个迭代器,而迭代器是一种取出元素的方式
该方法本质:
由于获取集合元素的步骤较多,不是一个方法能够解决的,因此该功能由一个内部类实现,该内部类实现了Iterator接口,复写了next,hasNext,remove方法,通过iterator()方法获取该内部类对象,该获取类操作的都是外部类的私有的数据部分,因此设为内部类最合适
直接方法返回数据:
也是可以的,但是这样会导致该方法除了有获取的功能,还要有判断的等等,破坏了单一性原则,且外界可以通过该方法直接访问数据,并不安全,因此使用内部类来达到获取的功能更合适
5.判断
contains,containsAll,isEmpty,equals
equals是复写的,用于判断两个集合内的内容是否一致,是通过比较来判断的,也就是说如果内容一致,但是顺序不一致一样被视为不相等,如果集合内存放的是对象,那么比较的是对象的地址


常用方法测试:

测试代码:

class ListTest
{
	public static void main(String[] args) 
	{
		ArrayList al = new ArrayList();
		al.add("helong001");
		al.add("helong002");
		al.add("helong003");
		al.add("helong004");

		//增删改查获取
		//增加,可以默认或者指定位置的增加一个或多个数据
		al.add("helong005");
		al.add(3,"helong006");
		ArrayList temp=new ArrayList();temp.add("ldy001");temp.add("ldy002");
		al.add(4,temp);
		sop(al);

		//三种获取方式iterator,listIterator,get
		for(Iterator it = al.iterator();it.hasNext();)
		{
			Object obj = it.next();
			if(obj.equals("helong005"))
				it.remove();
		}
		sop(al);
		/*
		1.集合的add是添加到末尾,而列表迭代器的add是添加到当前元素的后面
		2.集合的set需要提供改变的元素的位置索引,而列表迭代器不需要,它就是修改当前元素
		3.集合的remove需要提供索引或者元素值,而列表迭代器是删除当前元素
		4.如果通过add传入了一个Collection子类对象,那么该对象在集合中作为一个元素存在
			例如下例子中,我们无法匹配到"ldy001",因为本质上集合中没有这个对象的地址存在,
			有的是temp的地址。
		*/
		//列表迭代器是List体系特有的,类似于一个加强版的迭代器,功能更丰富
		//这也是得益于索引的存在
		ListIterator lit = al.listIterator();
		while(lit.hasNext())
		{
			Object obj = lit.next();
			if(obj.equals("helong002"))
				lit.add("helongadd");
			else if(obj.equals("helong006"))
				lit.set("helongset");
			else if(obj.equals("helong003"))
				lit.remove();
			else if(obj.equals("ldy001"))
				lit.set("ldyset");
			else if(obj.equals(temp))
				lit.set("add另一个集合到该集合中,在该集合中作为一个对象存在");
		}
		sop(al);
		sop("======================");
		while(lit.hasPrevious())
		{
			sop(lit.previous());
		}

		sop("======================");
		for(int i=0;i<al.size();i++)
		{
			sop(al.get(i));
		}

		//测试两个删除操作
		ArrayList all=new ArrayList();
		all.add(123);all.add(123);
		//all.remove(123);//jvm调用的是remove(int index)报异常,下标越界
		sop(all);
		f("adf");
	}
	private static  void f(String str){sop("f(String str)");}
	private static  void f(Object obj){sop("f(Object obj)");}
	private static void sop(Object obj)
	{
		System.out.println(obj);
	}
}

运行图:



6.特有方法:

List集合特有方法listIterator()返回它特有的迭代器ListIterator:
某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection,只限迭代,Vector的枚举方法不会报异常,但结果也是不确定的,因此不建议这么用。
在迭代时,不能通过集合中的方法操作集合中的元素,因为此时这些元素正被迭代器操作,如果此时使用集合方法操作元素会发生并发异常。只能使用迭代器的方法,但是迭代器方法有限,如果需要添加,修改等操作,则需要使用其子类接口,也就是ListIterator,该接口拥有增删改查等方法来操作集合中的数据,之所以它能进行增删改查是因为该体系元素都是有下标的
集合方法和列表迭代器方法对比:
1.集合的add是添加到末尾,而列表迭代器的add是添加到当前元素的后面
2.集合的set需要提供改变的元素的位置索引,而列表迭代器不需要,它就是修改当前元素
3.集合的remove需要提供索引或者元素值,而列表迭代器是删除当前元素
4.如果通过add传入了一个Collection子类对象,那么该对象在集合中作为一个元素存在
遍历元素的方法:
1.迭代器:iterator();
2.列表迭代器:listIterator();
3.集合方法:get()
[4.Vector特有:枚举方式:elements()]
 


5.List集合子类

ArrayList:底层使用数组结构存储数据。
特点:查询、修改速度快,使用下标访问很方便,但是插入、删除速度很慢。
Vector(已被ArrayList代替):底层是数组数据结构。
功能与ArrayList一模一样,JDK1.0出现的,而集合框架是1.2版本的,且Vector是线程同步的,效率低,因此使ArrayList,不使用Vector,同时Vector是唯一可以使用枚举取出集合中变量的一个集合类,枚举的功能与迭代器相同,但是由于枚举以及其方法名称过长,以被迭代器取代。
ArrayList和Vector特点:
初始都创建长度为10的数组,当所需长度超过10时,ArrayList做法是new一个15长度的数组,将原本的元素copy进去,再添加新的元素,Vector做法也是如此,只是Vector是开辟一个20长度的数组,所以ArrayList既能使长度可变,又比较节约空间,因此选用ArrayList。
LinkedList:底层使用链表结构存储数据。
特点:插入、删除速度快,因为只需修改两个节点,但是查询、修改很慢。
特有方法:
addFirst,addLast,getFitst,getLast(获得元素,但不删除),removeFirst,removeLast(获得元素,并且删除它)。之所以有这些方法,是因为对于链表,取头尾很简单。
注意:由于上述方法在链表为空时使用会报异常NoSuchElementException,因此不推荐使用。
JDK1.6后出现了替代方法:
addFirst,addLast----->offerFirst,offerLast
getFirst,getLast------>peekFirst,peekLast
removeFirst,removeLast------>pollFitst,pollLast
推荐使用新方法,这些方法会链表为空时使用会返回null。

6.综合小练习

三个子类的使用:

package com.helong.collectiondemo;

import java.util.*;
class Person
{
	private String name=null;
	Person(String name){this.name=name;}
}
class CollectionDemo 
{
	public static void main(String[] args) 
	{
		ArrayList al1 = new ArrayList();
		al1.add("123");
		al1.add("何龙");
		al1.add(23.23);
		al1.add(true);
		al1.add(new Person("LDY"));

		sop(al1.size());
		sop(al1);

		//判断
		sop("al1是否包含23.23:"+al1.contains(23.23));
		ArrayList al2 = new ArrayList();
		al2.add(true);
		al2.add(23.23);
		al2.add("123");
		sop("all是否包含23.23,true,字符串123:"+al1.containsAll(al2));
		sop("al1字符串是否为空:"+al1.isEmpty());
		sop("al1字符串是否与al2相等:"+al1.equals(al2));
		ArrayList al3=new ArrayList();
		ArrayList al4=new ArrayList();Person p1=new Person("aaa");
		al3.add(1);al3.add(2);al3.add(p1);
		al4.add(1);al4.add(2);al4.add(p1);
		sop("al3字符串是否与al4相等:"+al3.equals(al4));

		//删除
		al1.remove("123");
		sop("al1中删除字符串123:"+al1);
		al1.removeAll(al2);
		sop("al1中删除al2的部分:"+al1);
		al2.add("何龙");
		sop("al1:"+al1);
		sop("al2:"+al2);
		al1.retainAll(al2);
		sop("取al1和al2的交集:"+al1);

		//迭代器
		//迭代器是一个类,也就是说iterator方法返回这个类的对象,而该类是一个内部类,在ArrayList内部实现的,
		//实现了Iterator接口,拥有next,hasNext,remove方法,用于处理获取的功能。
		sop("使用迭代器取出al2中的数据:");
		for(Iterator it = al2.iterator();it.hasNext();)
		{
			sop(it.next());
		}

		/*
		1.add(index,element);addAll(index,collection);
		2.remove(index);
		3.indexOf(element);get(index);listInterator();
		4.set(index,element);
		*/
		//List体系特有的方法,由于该体系是有序的,可以重复的,因此
		//它的特有方法有一个共同点:参数都有下标值。
		al2.add(3,"muhaha");
		sop(al2);
		al2.remove(1);
		sop(al2);
		al2.set(0,'b');
		sop(al2);
		sop(al2.indexOf('b'));
		sop(al2.get(3));
		for(ListIterator lit = al2.listIterator();lit.hasNext();)
		{
			sop(lit.next());
		}

		//List体系特有的迭代器:ListIterator,该迭代器是Iterator的子类接口,
		//拥有更丰富的方法,用于对遍历中对元素进行操作。
		//对集合进行遍历,如果遇到java3,则添加一个java33,如果遇到java4则将java4改成java44,
		Person pp=new Person("helong");
		ArrayList al5=new ArrayList();al5.add("java1");al5.add("java2");al5.add("java3");al5.add("java4");al5.add(pp);
		sop(al5);
		ListIterator lit = al5.listIterator();
		for(;lit.hasNext();)
		{
			Object obj=lit.next();
			if(obj.equals("java3"))lit.add("java33");//列表迭代器的add是将元素插入到当前元素的后面
			else if(obj.equals("java4"))lit.set("java44");//列表迭代器的set用传入的参数代替当前元素
			else if(obj.equals(pp))lit.set("000");//equals比较对象时比较的仍是地址
		}
		sop(al5);
		sop(lit.hasNext());
		sop(lit.hasPrevious());
		while(lit.hasPrevious())
		{
			sop(lit.previous());
		}

		//测试Vector的枚举方式取到元素
		//这种取元素的方式是Vector特有的,与迭代器的功能是重复的,由于枚举的名字以及其方法的名字过长,
		//已被迭代器取代
		Vector v = new Vector();
		v.add("123");
		v.add("234");
		v.add("345");
		for(Enumeration e = v.elements();e.hasMoreElements();)
		{
			sop(e.nextElement());
		}

		//LinkedList练习
		LinkedList ll = new LinkedList();
		ll.add("add");
		ll.addFirst("addFirst");
		ll.offerFirst("offerFirst");
		ll.addLast("addLast");
		ll.offerLast("offerLast");
		sop(ll);
		sop(ll.getFirst());
		sop(ll.peekFirst());
		sop(ll.getLast());
		sop(ll.peekLast());
		sop(ll.size());
		ll.removeFirst();
		sop(ll+":长度:"+ll.size());
		ll.removeLast();
		sop(ll+":长度:"+ll.size());
		ll.pollFirst();
		sop(ll+":长度:"+ll.size());
		ll.pollLast();
		sop(ll+":长度:"+ll.size());
		//ll.removeLast();
		//ll.removeLast();
		//ll.removeLast();报异常;NoSuchElementException
		ll.pollLast();
		ll.pollLast();
		ll.pollLast();//不会报错,只是返回null,使用返回值前判断就可以了,因此推荐使用这个方法
	}
	private static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
运行图:



手写实现存储结构(数组和链表:数组的自动扩充,链表的节点等)

//手写实现存储结构(数组和链表:数组的自动扩充,链表的节点等)
package com.helong.mylist;
//==============================================
//ArrayList
//实现它的容量自动扩充
//增删改查长度
class MyArrayList
{
	private Object[] value=null;
	private int size=0;
	MyArrayList()
	{
		value=new Object[10];
	}
	//增加
	public boolean add(Object obj)
	{
		if(size==value.length)
			expansion();
		value[size++]=obj;
		return true;
	}
	//删除
	public boolean remove(int index)
	{
		if(index<0||index>=size)
			return false;
		Object[] temp=new Object[size-1];//使用到数组长度的地方要由size来替代,因为size才是真实的元素个数
		for(int i=0,j=0;i<size;i++)
		{
			if(i!=index)
			{
				temp[j++]=value[i];
			}
		}
		value=temp;
		size--;
		return true;
	}
	//内部调用remove(int index)
	public boolean remove(Object obj)
	{
		for(int i=0;i<size;i++)
		{
			if(value[i].equals(obj))
			{
				remove(i);
				return true;
			}
		}
		return false;
	}
	//修改
	public boolean set(int index,Object obj)
	{
		if(index<0||index>=size)
			return false;
		value[index]=obj;
		return true;
	}
	//获取
	public Object get(int index)
	{
		if(index<0||index>=size)
			return null;
		return value[index];
	}
	//长度
	public int length()
	{
		return size;
	}
	
	//复写toString方法,使得该类被打印时按照自定义方式输出
	public String toString()
	{
		String str="【";
		for(int i=0;i<size;i++)
		{
			if(i!=size-1)
				str+=(value[i]+",");
			else
				str+=value[i];
		}
		str+="】";
		return str;
	}
	private boolean expansion()
	{
		Object[] temp=new Object[value.length+5];
		temp=value.clone();
		//注意:clone只对一维数组起作用,而不能用于二维数组,
		//因为java没有二维数组的概念,而只有数组的数组,二维
		//数组存储的是几个一维数组的引用,而使用clone也只是
		//拷贝了这几个引用,说白了还是原来那几个一维数组对象。
		//如果想用于二维数组,那么就遍历其中的一维数组,挨个
		//拷贝一维数组到目标二维数组中的一维数组下。
		value=temp;
		return true;
	}
}

//==============================================
//LinkedList
//节点问题
//MyLinkedList内部有一个节点成员
//增删改查长度
class MyLinkedList
{
	private MyNode head=null;
	private MyNode tail=null;
	//增加
	public void add(Object obj)
	{
		if(head==null)
		{
			head=tail=new MyNode(null,obj);
		}
		else
		{
			MyNode temp=new MyNode(null,obj);
			tail.setNext(temp);
			tail=temp;
		}
	}
	public void addFirst(Object obj)
	{
		MyNode temp=new MyNode(head,obj);
		head=temp;
	}
	public void addLast(Object obj)
	{
		add(obj);
	}
	//删除并返回删除的元素
	public Object removeFirst()
	{
		if(head==null)return null;
		Object temp=head.getObj();
		head=head.getNext();
		return temp;
	}
	public Object removeLast()
	{
		if(tail==null)return null;
		Object temp=tail.getObj();
		MyNode node =head;
		while(node.getNext()!=tail)//查询到倒数第二个节点的位置
		{
			node=node.getNext();
		}
		tail=node;
		tail.setNext(null);//将该节点设为尾节点
		return temp;
	}
	//清空链表
	public void clear()
	{
		while(head!=null)
		{
			MyNode temp=head;
			head=head.getNext();
			temp.setNext(null);
		}
		tail=head;
	}
	//获取元素但不删除
	public Object getFirst()
	{
		return head==null?null:head.getObj();
	}
	public Object getLast()
	{
		return tail==null?null:tail.getObj();
	}
	//修改元素
	public Object setFirst(Object obj)
	{
		if(head!=null)
		head.setObj(obj);
		return head==null?null:obj;
	}
	public Object setLast(Object obj)
	{
		if(tail!=null)
		tail.setObj(obj);
		return tail==null?null:obj;
	}
	//复写了Object的toString方法,按照自定义方式打印输出
	public String toString()
	{
		String str="【";
		MyNode temp=head;
		while(temp!=null)
		{
			if(temp!=tail)
				str+=(temp.getObj()+",");
			else
				str+=temp.getObj();
			temp=temp.getNext();
		}
		str+="】";
		return str;
	}
}
class MyNode
{
	private MyNode next=null;
	private Object obj=null;
	MyNode(MyNode next,Object obj)
	{
		this.next=next;
		this.obj=obj;
	}
	public Object getObj()
	{
		return obj;
	}
	public MyNode getNext()
	{
		return next;
	}
	public void setObj(Object obj)
	{
		this.obj=obj;
	}
	public void setNext(MyNode next)
	{
		this.next=next;
	}
}


//==============================================
class  MyList
{
	public static void main(String[] args) 
	{
		//MyArrayList测试
		//增加
		printLine();
		sop("MyArrayList方法测试:");
		MyArrayList mal=new MyArrayList();
		mal.add(123);
		mal.add("234");
		mal.add(23.23);
		mal.add("345");
		sop(mal.length());
		sop(mal);
		//删除时,如果传入int,那默认是调用remove(int index)而不是remove(Object obj)
		mal.remove(23.23);
		sop(mal.length());
		sop(mal);
		mal.remove("345");
		sop(mal.length());
		sop(mal);
		//按照索引位置修改元素
		mal.set(0,"修改这里");
		sop(mal);
		//获取元素
		sop("索引为0的元素:"+mal.get(0));
		sop("索引为1的元素:"+mal.get(1));
		printLine();

		//============================================
		//MyLinkedList测试
		//增加
		sop("MyLinkedList方法测试:");
		MyLinkedList mll=new MyLinkedList();
		mll.add("123");
		mll.add(23.45);
		mll.add(true);
		sop(mll);
		mll.addFirst("start");
		mll.addLast("end");
		sop(mll);
		//删除头结点,尾节点
		sop("删除元素:"+mll.removeFirst());
		sop(mll);
		sop("删除元素:"+mll.removeLast());
		sop(mll);
		//获取元素
		sop("头元素:"+mll.getFirst());
		sop("尾元素:"+mll.getLast());
		//清空链表
		sop("清空链表");
		mll.clear();
		sop(mll);
		//当链表为空时,再取其头尾结点,此处定义成不会报异常会返回null
		sop("头元素:"+mll.getFirst());
		sop("尾元素:"+mll.getLast());
		//修改头尾结点值,注意此时链表并无数据,因此返回null
		sop("修改后头元素:"+mll.setFirst("head"));
		sop("修改后尾元素:"+mll.setLast("tail"));
		mll.add("ldy001");mll.add("ldy002");mll.add("ldy003");mll.add("ldy004");mll.add("ldy005");
		sop(mll);
		sop("修改后头元素:"+mll.setFirst("head"));
		sop("修改后尾元素:"+mll.setLast("tail"));
		sop(mll);
	}
	private static void sop(Object obj)
	{
		System.out.println(obj);
	}
	private static void printLine()
	{
		sop("============================================");
	}
}

运行图:


使用LinkedList模拟堆栈和队列数据结构

//使用LinkedList模拟堆栈和队列数据结构
//堆栈:特点先进后出
//队列:特点先进先出
package com.helong.mystackqueue;
import java.util.*;
class MyStack
{
	LinkedList value=new LinkedList();
	public void push(Object obj)
	{
		value.offerLast(obj);
	}
	public Object pop()
	{
		return value.pollLast();
	}
	public String toString()
	{
		return value.toString();
	}
}
class MyQueue
{
	LinkedList value=new LinkedList();
	public void push(Object obj)
	{
		value.offerLast(obj);
	}
	public Object pop()
	{
		return value.pollFirst();
	}
	public String toString()
	{
		return value.toString();
	}
}
class MyStackQueue 
{
	public static void main(String[] args) 
	{
		sop("======================================");
		//模拟堆栈的进栈,出栈
		MyStack ms = new MyStack();
		ms.push("no.1");
		ms.push("no.2");
		ms.push("no.3");
		sop(ms);
		sop("出栈:"+ms.pop());
		sop("出栈:"+ms.pop());
		sop("出栈:"+ms.pop());
		sop(ms);
		
		sop("======================================");
		//模拟队列的入队,出队
		MyQueue mq = new MyQueue();
		mq.push("队列no.1");
		mq.push("队列no.2");
		mq.push("队列no.3");
		sop(mq);
		sop("出队:"+mq.pop());
		sop("出队:"+mq.pop());
		sop("出队:"+mq.pop());
		sop(mq);
	}
	private static void sop(Object obj)
	{
		System.out.println(obj);
	}
}
运行图:




------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值