收集对象(Collection-->List、Set、Queue)

在 Java SE中,提供了数个收集对象的类,可以直接取用这些类,而不用重新打造类似的API。

1、Collection架构

Java SE提供了满足各种需求的API,在使用些API前,建议先了解其继承与接口操作架构,才能了解何时该采用哪个类,以及类之间如何彼此合作,而不会沦于死背API或抄写范例的窘境。 针对收集对象的需求, Java SE提供了 Collection API:

收集对象的行为,像是新增对象的add()方法、移除对象的remove()方法等,都是定义在java.util.Collection中。既然可以收集对象,也要能逐一取得对象,这就是java.lang.Iterable定义的行为,它定义了 iterator()方法返回java.uti1. Iterator操作对象,可以让你逐一取得收集的对象,详细操作方式,下一篇再做说明 收集对象的共同行为定义在collection中,然而收集对象会有不同的需求。如果希望收集时记录每个对象的索引顺序,并可依索引取回对象,这样的行为定义在java.util.List接口中。如果希望收集的对象不重复,具有集合的行为,则由java.util.Set定义。如果希望收集对象时以队列方式,收集的对象加入至尾端,取得对象时从前端,则可以使用java.util.Queue。如果希望对Queue的两端进行加入、移除等操作,则可以使用java util. Deque。
收集对象时,会依需求使用不同的接口操作对象。举例来说,如果想要收集时具有索引顺序,操作方式之一就是使用数组,而以组操作List的就是java.util.Arraylist。高度自定义的Java提供了AbstractCollection、AbstractList等,必要时可以继承AbstractCollection,操作自己的Collection,其他的API也类似。

2、List

List是一种Collection,可以收集对象,并以索引方式保留收集的对象顺序。 操作类有ArrayList,LinkedList。
1、ArrayList
数组在内存中会是连续的线性空间,根据索引随机存取时速度快,如果操作上有这类需求时,像是排序,就可使用 Arraylist,可得到较好的速度表现。 数组在内存中会是连续的线性空间,如果需要调整索引顺序时,会有较差的表现。例如若在已收集100对象的Arraylist中,使用可指定索引的add()方法,将对象新增到索引0位置,那么原先索引0的对象必须调整至索引1,索引1的对象必须调整至索引2,索引的对象必须调整至索引3。依此类推,使用 Arraylist做这类操作并不经济。 数组的长度固定也是要考虑的问题,在Arraylist内部数组长度不够时,会建立新数组,并将旧数组的参考指定给新数组,这也是必须耗费时间与内存的操作。为此, Arraylist有个可指定容量(Capacity)的构造函数,如果大致知道将收集的对象范围,事先建立足够长度的内部数组,可以节省以上所描述的成本。

2. Linkedlist

linkedlist在操作List接口时,采用了链接link结构。参考下面的可以更好理解link结构:

package coll_map;

public class LinkedListDemo {
	private class Node {
		Node(Object o) {
			this.o = o;
		}

		Object o;
		Node next;
	}

	private Node first;

	public void add(Object elem) {
		Node node = new Node(elem);
		if (first == null) {
			first = node;
		} else {
			append(node);
		}
	}

	private void append(Node node) {
		// TODO 自动生成的方法存根
		Node last = first;
		while (last.next != null) {
			last = last.next;
		}
		last.next = node;
	}

	private int size() {
		int count = 0;
		Node last = first;
		while (last != null) {
			last = last.next;
			count++;
		}
		return count;
	}

	public Object get(int index) {
		checkSize(index);
		return findElemOf(index);
	}

	private void checkSize(int index) throws IndexOutOfBoundsException {
		int size = size();
		if (index >= size) {
			throw new IndexOutOfBoundsException(String.format("Index:%d,Size:%d", index, size));
		}
	}
	
	private Object findElemOf(int index) {
		int count = 0;
		Node last = first;
		while (count < index) {
			last = last.next;
			count++;
		}
		return last.elem;//???
	}
}

在 simplelinkedlist内部使用Node封装新增的对象0,每次add()新增对象之后,将会形成链状结构目。 所以每次add()对象时,才会建立新的Node来保存对象,不会事先耗费内存,若调用size(),则从第一个对象,逐一参考下一个对象并计数,则可取得收集的对象长度。若想调用get()指定索引取得对象,则从第一个对象,逐一参考下一个对象并计数,则可取得指定索引的对象。 可以看出,想要指定索引随机存取对象时,链接方式都得使用从第一个元素开始查找下一个元素的方式,会比较没有效率,像排序就不适合使用链接操作的List。如果排序时,刚好必须将索引0与索引10000的素调换,效率就不高。 链接的每个元素会参考下一个元素,这有利于调整索引顺序。例如,若在已收集100对象的 simplelinkedlist中,操作可指定索引的add()方法,将对象新增到索引0位置。 新增的对象将建立Node实例封装,而frst(或上一节点的next)重新参考至新建的Node对象,新建Node的next则参考至下一node对象。因此,若收集的对象经常会有变动索引的情况,也许考虑链接方式操作的List会比较好,像是随时会有客户端登录或注销的客户端List,使用 Linkedlist会有比较好的效率

3、内容不重复的Set

同样是收集对象,在收集过程中若有相同对象,则不再重复收集,若有这类需求,可以使用set接口的操作对象。收集不重复单词:

package coll_map;

import java.util.*;

public class WordFind {
	public static void main(String[] args) {
		Scanner scan=new Scanner(System.in);
		
		System.out.println("输入英文");
		Set words =tokenSet(scan.nextLine());//调用tokenSet返回的是HashSet所有可以直接这样
		System.out.printf("不重单词%d个:%s%n", words.size(),words);
	}

	private static Set tokenSet(String line) {
		String[] tokens=line.split(" ");//根据空白切割出字符串,返回的是Spring[]
		return new HashSet(Arrays.asList(tokens));
	}
}

Arrays.asList ()方法返回List,而工List是一种Collection,因而可传给Hashset接受 Collection实例的构造函数,由于set的特性是不重复,因此若有相同单词,则不会再重复加入,最后只要调用set的size()方法,就可以知道收集的字符串个数, Hashset的 toString()操作,则会包括收集的字符串。

package coll_map;

import java.util.*;

class Student {
	private String name;
	private int ID;

	Student(String name, int ID) {
		this.name = name;
		this.ID = ID;
	}

	@Override
	public String toString() {
		// TODO 自动生成的方法存根
		return String.format("(%s,%s)", name, ID);
	}
}

public class Students {
	public static void main(String[] args) {
		Set<Student> stus=new HashSet<>();
		stus.add(new Student("gg1", 2017));
		stus.add(new Student("gg2", 2018));
		stus.add(new Student("gg3", 2019));
		stus.add(new Student("gg2", 2018));
		
		System.out.println(stus);
	}
}

set没有将重复学生数据排除,因为你并没有告诉set,什么样的 Student实例才算是重复,以 Hashset为例,会使用对象的 hashcode()与 equals()来判断对象是否相同。 Hashset的操作概念是,在内存中开设空间,每个空间会有个哈希编码( Hash Code)。
这些空间称为哈希桶( Hash Bucket),如果对象要加入 Hashset,则会调用对象的 hashcode)取得哈希码,并尝试放入对应号码的哈希桶中,如果哈希桶中没对象,则直接放入,如果哈希桶中有对象会再调用对象的equa1s()进行比较。 如果同一个哈希桶中已有对象,调用该对象 equals()与要加入的对象比较,结果为 false,则表示两个对象非重复对象,可以收集,如果是true,表示两个对象是重复对象,则不予收集事实上不只有 Hashset,Java中许多要判断对象是否重复时,都会调用 hashcode()与 equals()方法,因此规格书中建议,两个方法必须同时操作。以前面范例而言,若操作了 hashcode与 equals方法,则重复的 Student将不会被收集。IDE一般可以自动生成。
4、Queue
如果希望收集对象时以队列方式,收集的对象加入至尾端,取得对象时从前端,则可 add以使用 Queue接口的操作对象。 Queue继承自 collection,所以也具有 collection的 remove)、 element()等方法,然而 Queue定义了自己的 offer()、poll()与peek()等方法,最主要的差别之一在于,add()、 remove()、element()等方法操作失败时会抛出异常,而offer、poll()、()与peek()等方法操作失败时会返回特定值。 如果对象有操作 Queue,并打算以队列方式使用,且队列长度受限,通常建议使用 offer()、po1l()与peek()等方法。 offer()方法用来在队列后端加入对象,成功会返回true失败则返回 false。Poll方法用来取出队列前端对象,若队列为空则返回null,peek用来取得(但不取出)队列前端对象,若队列为空则返回null。 前面提过 Linkedlist它不仅操作了List接口,也操作了 Queue的行为,所以可将 linkedlist当作队列来使用。Queue<T> name = new LinkedList<>();

package coll_map;

import java.util.*;

interface Request {
	void execute();
}

public class RequestQueue {
	public static void main(String[] args) {
		Queue<Request> requests = new LinkedList<>();
		offerRequestTo(requests);
		process(requests);
	}

	static void offerRequestTo(Queue<Request> requests) {
		for (int i = 1; i <= 5; i++) {

			requests.offer(() -> System.out.printf("模拟产生数据:%f%n", Math.random())); // ----Lambda表达式----
		}
	}

	private static void process(Queue<Request> requests) {
		while (requests.peek() != null) {
			Request request = requests.poll();
			request.execute();
		}
	}
}

Deque
想对队列的前端与尾端进行操作,在前端加入对象与取出对象,在尾端加入对象与取出对象,Queue的子接口Deque就定义了这类行为。 Deque中定义 addfirst() removeFirst()、 getFirst()、 addlast()、 removelast()、 getLast( 等方法,操作失败时会抛出异常,而 offerfirst()、 polifirst(、 peekpirst()、 offerlast()、 polllast()、 peeklast()等方法,操作失败时会返回特定值 Qeue的行为与 Deque的行为有所重复,有几个操作是等义的:

QueueDeque
addaddLast
offerofferLast
removeremoveFirst
pollpollFirst
elementgetFirst
peekpeekFirst

使用ArrayDeque操作有限堆栈:

package coll_map;

import java.util.*;

public class Stack {
	private Deque<Object> elems=new ArrayDeque<>();
	private int capacity;
	
	public Stack(int capacity) {
		this.capacity=capacity;
	}
	
	public  boolean push(Object o) {
		if(isFull()) {
			return false;
		}
		return elems.offerLast(o);
	}
	
	private boolean isFull() {
		return elems.size()+1>capacity;
	}
	public Object pop() {
		return  elems.peekLast();
	}
	public int size() {
		return elems.size();
	}
	public static void main(String[] args) {
		Stack stack=new Stack(5);
		stack.push("one");
		stack.push("two");
		stack.push("three");
		System.out.println(stack.pop());
		System.out.println(stack.pop());
		System.out.println(stack.pop());
	}
}

转载于:https://my.oschina.net/u/3839146/blog/1799386

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值