本文共--字 阅读约--分钟 | 浏览: -- Last Updated: 2022-06-25
一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储,。另一方面,使用Array存储对象方面具有一些弊端,而Java集合就想一种容器,可以动态地把多个对象的引用放入容器中。
1、数组在内存存储方面的特点
2、数组在存储数据方面的弊端
Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。
Java集合可分为Collection
和Map
两种体系:
Collection
接口:单列数据,用来存储一个一个的对象
List
:有序的、可重复的集合
ArrayList
、LinkedList
、Vector
Set
:无序的、不可重复的集合
HashSet
、LinkedHashSet
、TreeSet
Map
接口:双列数据,保存具有映射关系键值对的集合,用来存储一对一对的数据。
HashMap
、LinkedHashMap
、TreeMap
、Hashtable
、Properties
import org.junit.Test;
import java.util.*;
/**
* Collection接口中方法的使用
*/
public class CollectionTest {
@Test
public void test() {
Collection coll = new ArrayList();
// 1、add(Object o); 添加元素
coll.add("AA");
coll.add(123); // int 会被自动装箱为 Integer类型
coll.add(new Date());
// 2、size(); 获取长度
System.out.println(coll.size()); // 3
Collection coll1 = new ArrayList();
coll1.add(456);
coll1.add("BB");
// 3、addAll(Collection c); 将c集合中的元素添加到当前集合中
coll.addAll(coll1);
System.out.println(coll.size()); // 5
System.out.println(coll); // "[AA, 123, Tue Jan 18 21:36:42 CST 2022, 456, BB]"
// 4、isEmpty(); 判断集合是否为空
System.out.println(coll.isEmpty()); // false 原理就是 return size == 0;
// 5、clear(); 清空结合元素
coll.clear();
System.out.println(coll.isEmpty()); // true
Collection coll3 = new ArrayList();
coll3.add(new String("CC"));
coll3.add(123);
coll3.add("DD");
// 6、contains(Object o); 判断集合是否存在o元素 原理是遍历使用equals进行比较
// 所以向Collection接口的实现类的对象中添加数据o时,要求o所在类重写equals方法
System.out.println(coll3.contains("DD")); // true
System.out.println(coll3.contains(123)); // true
System.out.println(coll3.contains(new String("CC"))); // true
System.out.println(coll3.contains("CC")); // true 判断的是值
Date d = new Date();
coll3.add(d);
System.out.println(coll3.contains(d)); // true
System.out.println(coll3.contains(new Date())); // true,Date包装类重写了equals方式比较的也是值 而不是地址
// 等同于 new Date().equals(new Date());
System.out.println("Date equals =>" + new Date().equals(new Date())); // true
Person1 p = new Person1("jack", 18);
coll3.add(p);
System.out.println(coll3.contains(p)); // true
System.out.println(coll3.contains(new Person1("jack", 18))); // false
// Person1类没有重写equals方法就会调用Object里面的equals 即进行引用的比较
// Object equals的实现就是 return this == obj;
// 7、containsAll(Collection c); 判断集合是否被当前集合所包含
Collection c1 = new ArrayList();
c1.add(123);
c1.add(345);
c1.add("AA");
// Arrays.asList(); 将参数集合转换成集合
Collection c2 = Arrays.asList(123, "AA");
System.out.println(c2.containsAll(c1)); // false
System.out.println(c1.containsAll(c2)); // true
// 8、remove(Object o); 删除一个元素,也会进行equals比较,相等才代表存在,才能被删除
boolean isSuccess = c1.remove(123);
boolean isSuccess2 = c1.remove(1234);
System.out.println(isSuccess); // true
System.out.println(isSuccess2); // false
// 9、removeAll(Collection c);
// 从当前集合中移出c集合中所有的元素,需要当前元素有,即移除当前集合中与集合c的交集,
// 即修改当前集合为 当前集合与集合c的差集
// 10、retainAll(Collection c); 求两个集合的交集,并只保存当前集合中存在于当前集合的元素,
// 即修改当前集合为 当前集合与集合c的交集。
// 11、equals(Object o); 比较当前集合与参数o是否是同一个集合(顺序也要求一致),是遍历进行equals比较
// 12、hashCode(); 返回当前集合的哈希值
Collection c3 = new ArrayList();
c3.add(123);
System.out.println(c3.hashCode()); // 154
c3.add("AA");
System.out.println(c3.hashCode()); // 6854
// 13、toArray(); 将集合转为数组
Object[] objects = c3.toArray();
for (int i = 0; i < objects.length; i++) {
System.out.println(objects[i]); // 123 AA
}
// 数组转换集合就是使用asList
List<String> strings = Arrays.asList(new String[]{"1", "2"});
System.out.println(strings.contains("1")); // true
System.out.println(strings.size()); // 2
System.out.println(strings); // [1, 2]
int[] arr = new int[]{1, 2, 3};
List<int[]> ints = Arrays.asList(arr); // adList([1, 2, 3])
System.out.println(ints.contains(1)); // false
System.out.println(ints.size()); // 输出是1 => 将arr当成一个元素了
System.out.println(ints); // [[I@1888ff2c] 打印的是地址值
Integer[] arr2 = new Integer[]{1, 2, 3};
List<Integer> integers = Arrays.asList(arr2);
System.out.println(integers.contains(1)); // true
System.out.println(integers.size()); // 3
System.out.println(integers); // [1, 2, 3]
// new int[]的是一个整型数组,这个arr整个才算是一个对象,而new Integer[] 是一个对象数组,每个元素都一个Integer对象
// 所有会被拆分成List的三个元素,从List<int[]> 和 List<Integer> 就可以看出来
// 前者的元素需要是int型数组,后者元素需要是Integer对象
}
}
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionIterator {
@Test
public void test1() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("hello"));
coll.add(false);
Iterator collIterator = coll.iterator();
// next方法
// System.out.println(collIterator.next()); // 123
// System.out.println(collIterator.next()); // 456
// System.out.println(collIterator.next()); // hello
// System.out.println(collIterator.next()); // false
// 遍历完成后在访问next就会报异常 NoSuchElementException异常
// System.out.println(collIterator.next());
// 使用for循环
// for (int i = 0; i < coll.size(); i++) {
// System.out.println(collIterator.next());
// }
// 推荐方式,使用while
// hasNext() 判断是否还有下一个元素
while (collIterator.hasNext()) {
// next(): 指针下移,将下移以后集合位置上的元素返回
System.out.println(collIterator.next());
}
}
@Test
public void test2() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new String("hello"));
coll.add(false);
Iterator collIterator = coll.iterator();
while (collIterator.hasNext()) {
Object obj = collIterator.next();
if ("hello".equals(obj)) {
// remove(): 删除集合中的数据,删除当前指针位置中的元素
// 调用的是迭代器对象的remove方法,而不是集合Collection的remove
collIterator.remove();
// 如果还未调用next() 指针还在初始位置 此时不指向任何元素 调用remove会报illegalStateException异常
// 调用了next()方法之后调用了remove方法,如果再调用一次remove方法也会报illegalStateException异常
// 因为此时被指针指向的元素已经被remove掉了,不能重复调用
}
}
// 想要再次遍历,需要重新生产迭代器对象
collIterator = coll.iterator();
while (collIterator.hasNext()) {
System.out.println(collIterator.next());
}
}
}
import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection;
public class ForTest {
@Test
public void test() {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add("hello");
// for (集合元素的类型 局部变量: 集合对象) {}
for(Object obj: coll) {
System.out.println(obj);
}
}
@Test
public void test2() {
int[] arr = new int[]{1,2,3,4,5};
for(int n: arr) {
System.out.println(n);
}
}
}
List接口是Collection的子接口之一,常用来存储有序的、可重复的数据,是动态的,实现该接口的常用类有ArrayList、LinkedList、Vector。
这三者的相同点是三个类都实现了List接口,存储数据的特点相同:存储有序的、可重复的数据。不同点如下:
ArrayList: 作为List接口的主要实现类;线程不安全的,效率高。底层使用Object[] elementData
存储
LinkedList: 对于频繁的插入、删除操作,使用此类效率比ArrayList高,底层使用双向链表存储。
Vector: 作为List接口的古老实现类;线程安全的,效率低。底层使用Object[] elementData
存储,与ArrayList不同的是,当需要扩容时,扩容至原来容量的两倍。
/* jdk7 */
ArrayList list = new ArrayList(); // 底层创建了长度是10的Object[]数组elementData
list.add(123); // elementData[0] = new Integer(123);
// ...
list.add(1); // 如果此次添加导致底层elementData数组容量不够,默认是10,则扩容
// 默认扩容为原来容量的1.5倍,同时需要将原有的数组中的数据复制到新的数组中
// 结论:建议开发中使用带参的构造器: ArrayList list = new ArrayList(int capacity);
/* jdk8 */
ArrayList list = new ArrayList(); // 底层Object[] elementData初始化为{},并没有创建长度为10的数组
list.add(123); // 第一次调用add()时,底层才创建了长度10的数组,并将数组123添加为elementData[0] = new Integer(123);
// 后续与jdk7一致
总结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省了内存。
LinkedList list = new LinkedList(); // 内部声明了Node尅性的first和last属性,默认值为null
list.add(123); // 将123封装到Node中,创建了Node对象
// 其中Node的定义如下, 即是一个双向链表
// private static class Node<E> {
// E item;
// Node<E> next;
// Node<E> prev;
// Node(Node<E> prev, E element, Node<E> next) {
// this.item = element;
// this.prev = prev;
// this.next = next;
// }
// }
void add(int index, Object ele)
: 在index位置插入ele元素boolean addAll(int index, Collection eles)
: 从index位置开始将eles中所有的元素都添加进来Object get(int index)
: 获取指定index位置的元素int indexOf(Object obj)
: 返回obj在集合中首次出现的位置,不存在返回-1。int LastIndexOf(Object obj)
: 返回obj在集合中末次出现的位置,不存在返回-1。Object remove(int index) / Object remove(Object obj)
: 移出指定index位置的元素 或者 删除指定元素(eg. list.remove(new Integer(123))
),并返回此元素。Object set(int index, Object ele)
: 设置指定index位置的元素为eleList subList(int fromIndex, int toIndex)
: 返回从fromIndex到toIndex位置的子集合Set接口也是Collection的子接口之一,用来存储无序的、不可重复的数据。Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
要求:
向Set中添加的数据,其所在的类一定要重写hashCode()
和equals()
方法,与存储数据时比较异同的方式有关。
重写的hashCode()
和equals()
方法要尽量保持一致性,即相等的对象必须具有相等的散列码(即哈希值)。
其常用的实现类有三个HashSet、LinkedHashSet、TreeSet。
HashSet: 作为Set接口的主要实现类;线程不安全的;可以存储null值。
import org.junit.Test;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetTest {
@Test
public void test1() {
Set s = new HashSet();
s.add(123);
s.add(456);
s.add(false);
s.add("AA");
s.add(new String("CC"));
s.add("BB");
s.add(new Person2("jack"));
s.add(123);
s.add(789);
s.add(new Person2("bob"));
s.add(new Person2("jack"));
Iterator iterator = s.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
// AA
// CC
// BB
// false
// 789
// 456
// com.cz.java.Person2@61a52fbd
// 123
// com.cz.java.Person2@233c0b17
// com.cz.java.Person2@63d4e2ba
// 无序性不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加。
// 而是根据数据的哈希值决定的
// 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即相同的元素只能添加一个
}
}
}
class Person2 {
String name;
Person2(String _name) {
this.name = _name;
}
}
添加元素的过程(以HashSet为例):
向HashSet中添加元素a,首先调用元素a所在类的hashCode()
方法,计算元素a的哈希值。
此哈希值接着通过某种算法计算出HashSet底层数组中的存放位置(即为索引位置,一般通过取模整个HashSet的容量来获得)
判断底层数组此位置上是否有元素,如果没有,则元素a添加成功。如果有其他元素b(或以链表形式存在的多个元素),如果哈希值不相同(取模的结果可能一样,如果链表形式存在多个,则需要多次比较),则元素a添加到此位置的链表上,如果哈希值相同,进而需要调用元素a所在类的equals方法,返回true,则添加失败,反之将元素a添加到此位置的链表上。
LinkedHashSet: 作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历。
存放数据的方式和HashSet一样,存放位置是无序性的,但是在存放的同时会额外添加两个引用,记录此数据的前一个数据和后一个数据,这样对于频繁的遍历操作,LinkedHashSet效率高于HashSet。
TreeSet: 要求放入的数据得是同一个类的实例对象,可以按照添加对象的指定属性,进行排序。
有两种排序方式:自然排序和定制排序。
自然排序中,比较两个对象是否有相同的标准为compareTo()
返回0则相同,不再是上面提到的equals()
;被添加到TreeSet中的对象(除内置类外),其类必须实现Comparable接口。
public class SetTest {
@Test
public void test2() {
TreeSet ts = new TreeSet();
ts.add(12);
ts.add(-12);
ts.add(241);
ts.add(24);
ts.add(5);
// ts.add("123"); // 会报错,必须是同一个类的实例对象
Iterator tsIterator = ts.iterator();
while(tsIterator.hasNext()) {
System.out.println(tsIterator.next());
// -12 5 12 24 241 // 添加数字会默认从小到大排序
}
}
@Test
public void test3() {
TreeSet ts = new TreeSet();
// java.lang.ClassCastException: class com.cz.java.Person2 cannot be cast to class java.lang.Comparable
// 添加对象时,如果添加的实例的类没有实现Comparable接口就会报错
ts.add(new Person2("jack"));
ts.add(new Person2("bob"));
ts.add(new Person2("andy"));
ts.add(new Person2("mike"));
ts.add(new Person2("jim"));
Iterator tsIterator = ts.iterator();
while(tsIterator.hasNext()) {
System.out.println(tsIterator.next());
// Person2{name='andy'}
// Person2{name='bob'}
// Person2{name='jack'}
// Person2{name='jim'}
// Person2{name='mike'}
}
}
}
class Person2 implements Comparable {
String name;
Person2(String _name) {
this.name = _name;
}
// 按照姓名从小到大排列
@Override
public int compareTo(Object o) {
if (o instanceof Person2) {
Person2 p2 = (Person2) o;
int compare = this.name.compareTo(p2.name);
if (compare != 0) {
return compare;
}
// 因为compareTo的比较是比较值,为0时则表示两个Person2对象的name值一样
// 此时应该换一种比较方式进行更深的比较
//
// 这里由于只有一个name属性 就return了一个 compare + 1,以保证两个相同name的Person2实例对象能当做不相等
// 实际场景中 应根据需要改写
return compare + 1;
} else {
throw new RuntimeException("输入的类型不匹配");
}
}
@Override
public String toString() {
return "Person2{" +
"name='" + name + '\'' +
'}';
}
}
定制排序中,需要传入一个Comparator的实例对象给TreeSet,并在其中的compare方法中定制自己的排序规则。
public class SetTest {
@Test
public void test4() {
Comparator comparator = new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return 0;
}
};
TreeSet ts = new TreeSet(comparator);
ts.add(new Person2("jack"));
ts.add(new Person2("bob"));
ts.add(new Person2("andy"));
ts.add(new Person2("mike"));
ts.add(new Person2("jim"));
Iterator tsIterator = ts.iterator();
while(tsIterator.hasNext()) {
System.out.println(tsIterator.next());
// 此时由于compare方法都返回的是0,所以就只有一个jack被添加并打印
}
}
}
import org.junit.Test;
import java.util.HashSet;
public class HashSetTest {
@Test
public void test1() {
HashSet set = new HashSet();
Person3 p1 = new Person3(1001, "AA");
Person3 p2 = new Person3(1002, "BB");
Object o = new Object();
set.add(p1);
set.add(p2);
System.out.println(set);
// [Person3{id=1002, name='BB'}, Person3{id=1001, name='AA'}]
p1.name = "CC";
set.remove(p1);
// 此时并没有remove掉p1,因为remove是否需要查找该元素是否存在,而此时又是HashSet,所以查找时,会先根据哈希值去找
// 而此时 name变为了CC,此时哈希值与存储时的name为AA的哈希值不一样了,所以不会找到,就remove了一个寂寞
// 需要注意的是,如果Person3类没有重写equals方法,此时会可以remove掉的。
// 因为默认会调用Object的equals方法比较的是引用地址 this == obj;
System.out.println(set);
// [Person3{id=1002, name='BB'}, Person3{id=1001, name='CC'}]
set.add(new Person3(1001, "CC"));
System.out.println(set);
// 修改成CC之后,其位置依旧没有改变,依然是那个以name为AA时计算的哈希值所确定的位置
// 此时add调用,又会根据name为CC计算一个哈希值,根据哈希值定位位置,显然该位置不会存在元素,然后add成功
// [Person3{id=1002, name='BB'}, Person3{id=1001, name='CC'}, Person3{id=1001, name='CC'}]
set.add(new Person3(1001, "AA"));
// 此时根据name为AA计算出哈希值,确定位置,发现位置上存在一个元素,然后进行equals比较
// 此时,由于p1存储时虽然name是AA,但是后来修改成了CC,此时进行equals比较则为false,则也添加成功,与原AA现CC的1001链在一起。
System.out.println(set);
// [Person3{id=1002, name='BB'}, Person3{id=1001, name='CC'}, Person3{id=1001, name='CC'}, Person3{id=1001, name='AA'}]
}
}
class Person3 {
int id;
String name;
public Person3() {
}
public Person3(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "Person3{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person3 person3 = (Person3) o;
if (id != person3.id) return false;
return name != null ? name.equals(person3.name) : person3.name == null;
}
@Override
public int hashCode() {
int result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
return result;
}
}
Map是双列数据,存储key-value对的数据。它的实现类如下:
HashMap:作为Map的主要实现类;线程不安全,效率高;存储null的key和value。jdk7之前,底层是数组+链表,jdk8之后,底层是数组+链表+红黑树。
TreeMap:要求key必须是同一个类创建的对象,因为要按照key进行排序,包含自然排序和定制排序。底层使用的是红黑树。
Hashtable:“古老”的实现类。线程安全,效率低;不能存储null的key和value。
Map结构的理解:
equals()
和hashCode()
方法。equals()
方法。HashMap的底层实现原理(以jdk7为例说明):
HashMap map = new HashMap();
// 在实例化以后,底层创建了长度是16的一维数组Entry[] table。
// ...可能已经执行了多次put
Person key1 = new Person("jack");
map.put(key1, 18);
// 首先调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。
// 如果此位置上的数据为空,此时的key1-value1添加成功,
// 如果不为空,意味此位置上存在一个或多个数据(以链表形式存在)
// 比较key1和已经存在的一个或多个数据的哈希值,
// 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功。
// 如果key1的哈希值与已经存在的某一个数据的哈希值相同,继续比较,调用key1所在类的equals方法,比较:
// 如果equals返回false,则添加成功。
// 如果equals返回true,使用value1替换相同key的value值,即修改相同key值的value,
// 即如果map.put(key1, 19),则key1与19组成key-value对。
// 在jdk7中,当key的哈希值相同时,以链表的方式存储。
// 在不断的添加过程中,会涉及到扩容问题(超出临界值时且要存放的位置非空值),默认的扩容方式是扩容为原来容量的2倍,并将原有的数据复制过来。
// jdk8相较于jdk7在底层实现方面的不同:
// 1. new HashMap()时底层没有创建一个长度为16的数组
// 2. jdk8底层的数组是 Node[] 而非 Entry[]
// 3. 首次调用put方法时,底层创建长度为16的数组
// 4. jdk7底层结构只有数组+链表,jdk8中底层结构为数组+链表+红黑树
// 当数组的某一个索引位置上的元素以链表形式存在的数据个数大于8 且 当前数组长度大于64时,
// 此时此索引位置上的所有数据改为使用红黑树存储,查找效率提高了。
// 源码中的常量
// DEFAULT_INITIAL_CAPACITY: HashMap的默认容量 16
// DEFAULT_LOAD_FACTOR: HashMap的默认加载因子 0.75
// threshold: 扩容的临界值,等于容量*加载因子,16 * 0.75 = 12
// TREEIFY_THRESHOLD: Bucket中里链表长度大于该默认值时,转化为红黑树,默认是8
// MIN_TREEIFY_CAPACITY: 桶中的Node被树化时最小的hash表容量 64
另外,在new HashSet()
的时候实际上是 new HashMap()
,add的时候的就是将数据当初map中的key去put,对应的value就是一个空对象new Object()
。
为什么重写equals()就一定要重写hashCode()方法?
在实际应用中,我们认为两个对象即使不是指向的同一块内存,只要这两个对象的各个字段属性值都相同,那么就认为这两个对象是同一个对象。所以就需要重写equals()方法,即如果两个对象指向内存地址相同或者两个对象各个字段值相同,那么就是同一个对象。
对于对象集合的判重,如果一个集合含有100个对象实例,仅仅使用equals()方法的话,那么对于一个对象判重就需要比较4950次,随着集合规模的增大,时间开销是很大的。但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的hashCode不相同,也不再需要调用equals()方法,从而大大减少了equals()比较次数。所以从程序实现原理上来讲的话,既需要equals()方法,也需要hashCode()方法。那么既然重写了equals(),那么也要重写hashCode()方法,以保证两者之间的配合关系。
源码中,使用Entry类before和after属性来记录元素的先后顺序,该Entry类继承与HashMap.Node。
Object put(Object key, Object value)
:将指定key-value添加到(或修改)当前Map对象中。void putAll(Map m)
: 将m中的所有key-value对存放到当前map中Object remove(Object key)
: 移出指定key的key-value对,并返回value。void clear()
:清空当前map中的所有数据。Object get(Object key)
:获取指定key对应的valueboolean containsKey(Object key)
:是否包含指定的keyboolean containsValue(Object value)
:是否包含指定的valueint size()
:返回map中key-value对的个数boolean isEmpty()
:判断当前map是否为空boolean equals(Object obj)
: 判断当前map和参数对象obj是否相等Set keySet()
:返回所有key构成的Set集合Collection values()
:返回所有value构成的Collection集合Set entrySet()
:返回所有key-value对构成的Set集合所以Map的遍历如下:
import org.junit.Test;
import java.util.*;
public class MapTest {
@Test
public void test1() {
Map map = new HashMap();
map.put("AA", 123);
map.put(45, 123);
map.put("BB", 456);
// 遍历所有的key集
Set s = map.keySet();
Iterator iterator = s.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
// 遍历所有value集
Collection values = map.values();
for(Object obj: values) {
System.out.println(obj);
}
// 遍历所有的key-value对
Set s2 = map.entrySet();
Iterator iterator2 = s2.iterator();
while(iterator2.hasNext()) {
Object obj = iterator2.next();
// 本身是一个Entry,所以可以如下强转
// Map.Entry entry = (Map.Entry) obj;
// System.out.println(entry.getKey() + " ---> " + entry.getValue())
System.out.println(obj);
}
}
}
import java.io.FileInputStream;
import java.util.Properties;
public class PropertiesTest {
public static void main(String[] args) throws Exception {
// 处理配置文件,key-value都是String类型
Properties pros = new Properties();
FileInputStream file = new FileInputStream("jdbc.properties");
pros.load(file); // 加载流对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println(name); // Tom
System.out.println(password); // abc123
}
}
对应的jdbc.properties
就是一个配置文件,如下
name=Tom
password=abc123
是一个操作Set、List和Map等集合工具类。常用方法如下:
reverse(List list)
:反转list中元素的顺序。
shuffle(List list)
:对list集合元素进行随机排序。
sort(List list)
:根据元素的自然顺序对list集合元素按升序排序
sort(List list, Comparator comparator)
:根据指定的comparator产生的顺序对list集合元素进行排序
swap(List list, int i, int j)
:将指定list集合中的i处元素和j处元素进行交换
Object max(Collection coll)
: 根据元素的自然顺序,返回给定集合中的最大元素。
Object max(Collection coll, Comparator comparator)
: 根据comparator指定的顺序自然顺序,返回给定集合中的最大元素。
Object min(Collection coll)
: 根据元素的自然顺序,返回给定集合中的最小元素。
Object min(Collection coll, Comparator comparator)
: 根据comparator指定的顺序自然顺序,返回给定集合中的最小元素。
int frequency(Collection coll, Object obj)
:返回指定集合中指定元素的出现次数
void copy(List dest, List src)
: 将src中的内容复制到dest中
boolean replaceAll(List list, Object oldVal, Object newVal)
: 使用新值替换Listzh中的所有对应的旧值。
部分方式代码演示如下:
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class CollectionsTest {
@Test
public void test1() {
List list = new ArrayList();
list.add(123);
list.add(145);
list.add(1);
list.add(-23);
list.add(-34);
list.add(5);
Collections.reverse(list);
System.out.println(list); // [5, -34, -23, 1, 145, 123]
Object[] objects = new Object[list.size()];
List dest = Arrays.asList(objects); // 使用空对象数组站位,以免报异常
Collections.copy(dest, list);
System.out.println(dest); // [5, -34, -23, 1, 145, 123]
Collections.replaceAll(list, 5, 3);
System.out.println(list); // [3, -34, -23, 1, 145, 123]
// Collections类中提供了多个 synchronizedXxx()方法
// 如synchronizedList,synchronizedMap,synchronizedSet
// 该方法可将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题
List list1 = Collections.synchronizedList(list);
// 此时的list1就是线程安全的
}
}