目录
LinkedList容器介绍
Set接口介绍
HashSet容器的使用
通过HashSet存储自定义对象
TreeSet容器的使用
LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两 个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中 的任意一个节点开始,都可以很方便地找到所有节点。
LinkedList的存储结构图
每个节点都应该有3部分内容:
class Node {
Node previous; //前一个节点
E element; //本节点保存的数据
Node next; //后一个节点
}
List实现类的选用规则
如何选用ArrayList、LinkedList、Vector?
1 需要线程安全时,用Vector。
2 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)
3 不存在线程安全问题时,增加或删除元素较多用LinkedList
LinkedList容器的使用(List标准)
LinkedList实现了List接口,所以LinkedList是具备List的存储特征的 (有序,元素有重复)。
public class LinkedListTest {
public static void main(String[] args) {
//实例化LinkedList容器
List list = new LinkedList<>();
//添加元素
boolean a = list.add("a");
boolean b = list.add("b");
boolean c = list.add("c");
list.add(3,"a");
System.out.println(a+"\t"+b+"\t"+c);
for(int i=0;i
LinkedList容器的使用(非List标准)
public class LinkedListTest2 {
public static void main(String[] args) {
System.out.println("-------LinkedList-------------");
//将指定元素插入到链表开头
LinkedList linkedList1 = new LinkedList<>();
linkedList1.addFirst("a");
linkedList1.addFirst("b");
linkedList1.addFirst("c");
for (String str:linkedList1){
System.out.println(str);
}
System.out.println("----------------------");
//将指定元素插入到链表结尾
LinkedList linkedList = new LinkedList<>();
linkedList.addLast("a");
linkedList.addLast("b");
linkedList.addLast("c");
for (String str:linkedList){
System.out.println(str);
}
System.out.println("---------------------------");
//返回此链表的第一个元素
System.out.println(linkedList.getFirst());
//返回此链表的最后一个元素
System.out.println(linkedList.getLast());
System.out.println("-----------------------");
//移除此链表中的第一个元素,并返回这个元素
linkedList.removeFirst();
//移除此链表中的最后一个元素,并返回这个元素
linkedList.removeLast();
for (String str:linkedList){
System.out.println(str);
}
System.out.println("-----------------------");
linkedList.addLast("c");
//从此链表所表示的堆栈处弹出一个元素,等效于removeFirst
linkedList.pop();
for (String str:linkedList){
System.out.println(str);
}
System.out.println("-------------------");
//将元素推入此链表所表示的堆栈 这个等效于addFisrt(E e)
linkedList.push("h");
for (String str:linkedList){
System.out.println(str);
}
}
}
LinkedList的源码分析
添加元素
private static class Node {
E item;
Node next;
Node prev;
Node(Node prev, E element, Node next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
成员变量
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node last;
添加元素
/**
* Appends the specified element to the end of this list.
*
* This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
头尾添加元素
addFirst
/**
* Inserts the specified element at the beginning of this list.
*
* @param e the element to add
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node f = first;
final Node newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
addLast
/**
* Appends the specified element to the end of this list.
*
* This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
public void addLast(E e) {
linkLast(e);
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node l = last;
final Node newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
获取元素
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Tells if the argument is the index of an existing element.
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
/**
* Returns the (non-null) Node at the specified element index.
*/
Node node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
Set接口继承自Collection接口,Set接口中没有新增方法,它和 Collection接口保持完全一致。我们在前面学习List接口的使用方 式,在Set中仍然适用。因此,学习Set的使用将没有任何难度。
Set接口特点
Set特点:无序、不可重复。无序指Set中的元素没有索引,我们只 能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新 元素如果和Set中某个元素通过equals()方法对比为true,则只能保 留一个。
Set常用的实现类有:HashSet、TreeSet等,我们一般使用 HashSet。
HashSet是Set接口的实现类。是Set存储特征的具体实现。
public class HashSetTest {
public static void main(String[] args) {
//实例化HashSet
Set set = new HashSet<>();
//添加元素
set.add("a");
set.add("b1");
set.add("c2");
set.add("d");
set.add("a");
//获取元素,在Set容器中没有索引,所以没有对应的get(int index)方法
for(String str: set){
System.out.println(str);
}
System.out.println("--------------------");
//删除元素
boolean flag = set.remove("c2");
System.out.println(flag);
for(String str: set){
System.out.println(str);
}
System.out.println("------------------------");
int size = set.size();
System.out.println(size);
}
}
HashSet存储特征分析
HashSet 是一个不保证元素的顺序且没有重复元素的集合,是线程 不安全的。HashSet允许有null 元素。
无序:
在HashSet中底层是使用HashMap存储元素的。HashMap底层使 用的是数组与链表实现元素的存储。元素在数组中存放时,并不是 有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定 元素在数组中的位置。
不重复:
当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会 调用元素的equals()方法判断两个元素是否相同。如果元素相同则 不会添加该元素,如果不相同则会使用单向链表保存该元素。
创建Users对象
public class Users {
private String username;
private int userage;
public Users(String username, int userage) {
this.username = username;
this.userage = userage;
}
public Users() { }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Users users = (Users) o;
if (userage != users.userage) return false;
return username != null ? username.equals(users.username) : users.username == null;
}
@Override
public int hashCode() {
int result = username != null ? username.hashCode() : 0;
result = 31 * result + userage;
return result;
}
public String getUsername() {
return username;
}
public void setUsername(String username)
{
this.username = username;
}
public int getUserage() {
return userage;
}
public void setUserage(int userage) {
this.userage = userage;
}
@Override
public String toString() {
return "Users{" +
"username='" + username + '\'' +
", userage=" + userage +
'}';
}
}
在HashSet中存储Users对象
public class HashSetTest2 {
public static void main(String[] args) {
//实例化HashSet
Set set = new HashSet<>();
Users u = new Users("oldlu",18);
Users u1 = new Users("oldlu",18);
set.add(u);
set.add(u1);
System.out.println(u.hashCode());
System.out.println(u1.hashCode());
for(Users users:set){
System.out.println(users);
}
}
}
HashSet底层源码分析
成员变量
private transient HashMap map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
添加元素
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element e to this set if
* this set contains no element e2 such that
* (e==null ? e2==null : e.equals(e2)) .
* If this set already contains the element, the call leaves the set
* unchanged and returns false.
*
* @param e element to be added to this set
* @return true if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
TreeSet实现了Set接口,它是一个可以对元素进行排序的容器。底 层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap, 通过key来存储元素。 TreeSet内部需要对存储的元素进行排序,因 此,我们需要给定排序规则。
排序规则实现方式:
1、通过元素自身实现比较规则。
2、通过比较器指定比较规则。
public class TreeSetTest {
public static void main(String[] args) {
//实例化TreeSet
Set set = new TreeSet<>();
//添加元素
set.add("c");
set.add("a");
set.add("d");
set.add("b");
set.add("a");
//获取元素
for(String str :set){
System.out.println(str);
}
}
}