[转]Java集合的五点体会

本文探讨了Java集合中常见的五个误区,包括List与数组的区别、Iterator的高级用法、Iterable的灵活应用、HashMap键的不可变性和SortedSet中equals与Comparable的区别。

 

The Collections classes in java.util were designed to help, namely by replacing arrays and, thus, improving Java performance. As you learned in the previous article, they're also malleable, willing to be customized and extended in all kinds of ways, in service of good, clean code.

Collections are also powerful, however, and mutable: use them with care and abuse them at your own risk.

1. Lists aren't the same as arrays List不同于数组,比如删除这个操作。

Java developers frequently make the mistake of assuming thatArrayList is simply a replacement for the Java array. Collections are backed by arrays, which leads to good performance when looking up items randomly within a collection. And, like arrays, collections use integer-ordinals to obtain particular items. Still, a collection isn't a drop-in replacement for an array.

The trick to differentiating collections from arrays is knowing the difference between order and position. For example, List is an interface that preserves the order in which items are placed into a collection, as Listing 1 shows:


Listing 1. Mutable keys

import java.util.*;

 

public class OrderAndPosition

{

    public static <T> void dumpArray(T[] array)

    {

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

        for (int i=0; i<array.length; i++)

            System.out.println("Position " + i + ": " + array[i]);

    }

    public static <T> void dumpList(List<T> list)

    {

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

        for (int i=0; i<list.size(); i++)

            System.out.println("Ordinal " + i + ": " + list.get(i));

    }

   

    public static void main(String[] args)

    {

        List<String> argList = new ArrayList<String>(Arrays.asList(args));

 

        dumpArray(args);

        args[1] = null;

        dumpArray(args);

       

        dumpList(argList);

        argList.remove(1);

        dumpList(argList);

    }

}

 

When the third element is removed from the above List, the other items "behind" it slide up to fill the empty slots. Clearly, this collections behavior differs from that of an array. (In fact, removing an item from an array is itself not quite the same thing as removing it from a List — "removing" an item from an array means overwriting its index slot with a new reference or null.)

 

2. Iterator, you surprise me!

 

ListIterator,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置。ListIterator 没有当前元素;它的光标位置 始终位于调用 previous()所返回的元素和调用 next() 所返回的元素之间。在长度为 n 的列表中,有 n+1 个有效的索引值,从 0  n(包含)。

 

There's no doubt that Java developers love the Java Collections Iterator, but when was the last time you really looked at theIterator interface? Most of the time, we just slap Iterator inside a for() loop or enhanced for() loop and move on, so to speak.

But, for those who go digging, Iterator has two surprises in store.

First, Iterator supports the ability to remove an object from a source collection safely, by calling remove() on the Iteratoritself. The point here is to avoid a ConcurrentModifiedException, which signals precisely what its name implies: that a collection was modified while an Iterator was open against it. Some collections will let you get away with removing or adding elements to a Collection while iterating across it, but calling remove() on the Iterator is a safer practice.

Second, Iterator supports a derived (and arguably more powerful) cousin. ListIterator, only available from Lists, supports both adding and removing from a List during iteration, as well as bidirectional scrolling through Lists.

Bidirectional scrolling can be particularly powerful for scenarios such as the ubiquitous "sliding set of results," showing 10 of many results retrieved from a database or other collection. It can also be used to "walk backwards" through a collection or list, rather than trying to do everything from the front. Dropping in a ListIterator is much easier than using downward-counting integer parameters to List.get() to "walk backwards" through a List.

 

3. Not all Iterables come from collections读文件内容的实用类

Ruby and Groovy developers like to brag about how they can iterate across a text file and print its contents to the console with a single line of code. Most of the time, they say, doing the same thing in Java programming takes dozens of lines of code: open aFileReader, then a BufferedReader, then create a while() loop to call getLine() until it comes back null. And, of course, you have to do all this in a try/catch/finally block that will handle exceptions and close the file handle when finished.

It may seem like a silly and pedantic argument, but it does have some merit.

What they (and quite a few Java developers) don't know is that not all Iterables have to come from collections. Instead, anIterable can create an Iterator that knows how to manufacture the next element out of thin air, rather than blindly handing it back from a pre-existing Collection:


Listing 2. Iterating a file

// FileUtils.java

import java.io.*;

import java.util.*;

 

public class FileUtils

{

    public static Iterable<String> readlines(String filename)

       throws IOException

    {

       final FileReader fr = new FileReader(filename);

       final BufferedReader br = new BufferedReader(fr);

      

       return new Iterable<String>() {

             public <code>Iterator</code><String> iterator() {

                   return new <code>Iterator</code><String>() {

                         public boolean hasNext() {

                               return line != null;

                         }

                         public String next() {

                               String retval = line;

                               line = getLine();

                               return retval;

                         }

                         public void remove() {

                               throw new UnsupportedOperationException();

                         }

                         String getLine() {

                               String line = null;

                               try {

                                     line = br.readLine();

                               }

                               catch (IOException ioEx) {

                                     line = null;

                               }

                               return line;

                         }

                         String line = getLine();

                   };

             }    

       };

    }

}

 

//DumpApp.java

import java.util.*;

 

public class DumpApp

{

    public static void main(String[] args)

        throws Exception

    {

        for (String line : FileUtils.readlines(args[0]))

            System.out.println(line);

    }

}

 

This approach has the advantage of not holding the entire contents of a file in memory, but with the caveat that, as written, it doesn't close() the underlying file handle. (You could fix this by closing whenever readLine() returns null, but that won't solve cases where Iterator doesn't run to completion.)

 

4. Beware the mutable hashCode() 不要改hashmap的key值

Map is a wonderful collection, bringing us the niftiness of key/value pair collections often found in other languages like Perl. And the JDK gives us a great Map implementation in the form of the HashMap, which uses hashtables internally to support fast key lookups for corresponding values. But therein lies a subtle problem: Keys that support hash codes dependent on the contents of mutable fields are vulnerable to a bug that will drive even the most patient Java developer batty.

Assuming the Person object in Listing 3 has a typical hashCode() (which uses the firstNamelastName, and age fields — all non-final — to calculate the hashCode()), the get() call to Map will fail and return null:


Listing 3. Mutable hashCode() drives me buggy

// Person.java

import java.util.*;

 

public class Person

    implements Iterable<Person>

{

    public Person(String fn, String ln, int a, Person... kids)

    {

        this.firstName = fn; this.lastName = ln; this.age = a;

        for (Person kid : kids)

            children.add(kid);

    }

   

    // ...

   

    public void setFirstName(String value) { this.firstName = value; }

    public void setLastName(String value) { this.lastName = value; }

    public void setAge(int value) { this.age = value; }

   

    public int hashCode() {

        return firstName.hashCode() & lastName.hashCode() & age;

    }

 

    // ...

 

    private String firstName;

    private String lastName;

    private int age;

    private List<Person> children = new ArrayList<Person>();

}

 

 

// MissingHash.java

import java.util.*;

 

public class MissingHash

{

    public static void main(String[] args)

    {

        Person p1 = new Person("Ted", "Neward", 39);

        Person p2 = new Person("Charlotte", "Neward", 38);

        System.out.println(p1.hashCode());

       

        Map<Person, Person> map = new HashMap<Person, Person>();

        map.put(p1, p2);

       

        p1.setLastName("Finkelstein");

        System.out.println(p1.hashCode());

       

        System.out.println(map.get(p1));

    }

}

 

Clearly, this approach is a pain but the solution is easy: Never use a mutable object type as a key in a HashMap.

 

5. equals() vs Comparable

When cruising through the Javadocs, Java developers frequently happen across the SortedSet type (and its lone implementation in the JDK, the TreeSet). Because SortedSet is the only Collection in the java.util package that offers any sorting behavior, developers often begin using it without questioning the details too closely. Listing 4 demonstrates:


Listing 4. SortedSet, I'm so glad I found you!

 

import java.util.*;

 

public class UsingSortedSet

{

    public static void main(String[] args)

    {

        List<Person> persons = Arrays.asList(

            new Person("Ted", "Neward", 39),

            new Person("Ron", "Reynolds", 39),

            new Person("Charlotte", "Neward", 38),

            new Person("Matthew", "McCullough", 18)

        );

        SortedSet ss = new TreeSet(new Comparator<Person>() {

            public int compare(Person lhs, Person rhs) {

                return lhs.getLastName().compareTo(rhs.getLastName());

            }

        });

        ss.addAll(perons);

        System.out.println(ss);

    }

}

 

After working with this code for a while, you might discover one of the Set's core features: that it disallows duplicates. This feature is actually described in the Set Javadoc. A Set is a "collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2), and at most one null element."

But this doesn't actually seem to be the case — although none of the Person objects in Listing 4 are equal (according to theequals() implementation on Person), only three objects are present within the TreeSet when printed.

Contrary to the stated nature of the set, the TreeSet, which requires objects to either implement Comparable directly or have aComparator passed in at the time of construction, doesn't use equals() to compare the objects; it uses the compare orcompareTo methods of Comparator/Comparable.

So, objects stored in a Set will have two potential means of determining equality: the expected equals() method and theComparable/Comparator method, depending on the context of who is asking.

What's worse, it isn't sufficient to simply declare that the two should be identical, because comparison for the purpose of sorting isn't the same as comparison for the purpose of equality: It may be perfectly acceptable to consider two Persons equal when sorting by last name, but not equal in terms of their contents.

Always ensure that the difference between equals() and the Comparable.compareTo()-returning-0 is clear when implementing Set. By extension, the difference should also be clear in your documentation.

 

In conclusion

The Java Collections library is scattered with tidbits that can make your life much easier and more productive, if only you know about them. Unearthing tidbits often involves some complexity, however, like discovering that you can have your way withHashMap, just as long as you never use a mutable object type as its key.

So far, we've dug beneath the surface of Collections, but we haven't yet hit the gold mine: Concurrent Collections, introduced in Java 5. The next five tips in this series will focus on java.util.concurrent.

 

From http://www.ibm.com/developerworks/java/library/j-5things3/index.html

【电能质量扰动】基于ML和DWT的电能质量扰动分类方法研究(Matlab实现)内容概要:本文研究了一种基于机器学习(ML)和离散小波变换(DWT)的电能质量扰动分类方法,并提供了Matlab实现方案。首先利用DWT对电能质量信号进行多尺度分解,提取信号的时频域特征,有效捕捉电压暂降、暂升、中断、谐波、闪变等常见扰动的关键信息;随后结合机器学习分类器(如SVM、BP神经网络等)对提取的特征进行训练与分类,实现对不同类型扰动的自动识别与准确区分。该方法充分发挥DWT在信号去噪与特征提取方面的优势,结合ML强大的模式识别能力,提升了分类精度与鲁棒性,具有较强的实用价值。; 适合人群:电气工程、自动化、电力系统及其自动化等相关专业的研究生、科研人员及从事电能质量监测与分析的工程技术人员;具备一定的信号处理基础和Matlab编程能力者更佳。; 使用场景及目标:①应用于智能电网中的电能质量在线监测系统,实现扰动类型的自动识别;②作为高校或科研机构在信号处理、模式识别、电力系统分析等课程的教学案例或科研实验平台;③目标是提高电能质量扰动分类的准确性与效率,为后续的电能治理与设备保护提供决策依据。; 阅读建议:建议读者结合Matlab代码深入理解DWT的实现过程与特征提取步骤,重点关注小波基选择、分解层数设定及特征向量构造对分类性能的影响,并尝试对比不同机器学习模型的分类效果,以全面掌握该方法的核心技术要点。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值