2

This is probably easy for someone more experienced with LINQ and the Lambda expressions in C#. I'm just starting out with them, so I must be missing something.

I have a custom object like this:

public class BusinessName {
    public string Name { get; set; }
    public string Type { get; set; }
}

Then, I have a list of these objects:

List<BusinessName> businessNames = new List<BusinessName>();

I want to convert this into a list of string with the Name property of the BusinessName object:

List<string> names = new List<string>();

Obviously, I could do this:

foreach (BusinessName name in businessNames) {
    names.Add(name.Name);
}

But, I want to try using the lambda expressions with LINQ so that I can put this into a one liner.

So, I tried:

names.AddRange(businessNames.Select(item => item.Name));

This works as expected, but is much slower than the foreach loop by 2x. I suspect this is because it's iterating the list twice, unlike the foreach loop.

So, I started looking for an alternative. I found Concat() but couldn't figure out how it was any different than AddRange().

Does anyone know a way to do this in one pass with LINQ?


It's already clear that the behaviour is as per documented. But I think your main question is why is the behaviour different for ArrayList and TreeSet. Well, it has to do with how data is stored internally in both the collections.

An ArrayList internally uses an array to store the data, which is re-sized as the size of ArrayList dynamically increases. Now, when you create a subList of your given list, original list with the specified indices is associated with the subList. So, any structural changes (that screws the indexing of the original array), done in the original list, will make the index stored as a part of sublist meaningless. That is why any structural changes is not allowed in case of ArrayList#subList method. The subList method returns you an instance of an inner class named SubList inside the ArrayList class, which looks like:

private class SubList extends AbstractList<E> implements RandomAccess {
    private final AbstractList<E> parent;
    private final int parentOffset;
    private final int offset;
    int size;

    SubList(AbstractList<E> parent,
            int offset, int fromIndex, int toIndex) {
        this.parent = parent;
        this.parentOffset = fromIndex;
        this.offset = offset + fromIndex;
        this.size = toIndex - fromIndex;
        this.modCount = ArrayList.this.modCount;
    }

As you see, the SubList contains a reference to the original list. And the parentOffset is nothing but the starting index of the subList you are creating. Now modifying the original list will possibly change the value at fromIndex in original list, but not inside the SubList. In that case, parentOffset in SubList class and fromIndex in original list, will point to different array elements. It might also be possible that at some point the original array becomes shorter enough to invalidate index stored in the SubList and make it OutOfRange. This is certainly not desirable, and the semantics of the subList returned is considered undefined, on such structural changes to original list.

On the other hand, a TreeSet stores it's data internally in a TreeMap. Now as there is no such concept of indices in a Map, there is no issue of indices breaking up. A Map is nothing but a mapping of key-value pair. Creating a SubSet involves creating a SubMap which is backed by the original Map. Modifying the original Set will just require the corresponding key-value mapping being invalidated, thus propagating the changes to the subMap created for the subSet.

4

5 に答える 5

1

が空の場合names、あなたがすることは次のとおりです。

var names = businessNames.Select(item => item.Name).ToList();

別のオプションは、次のようにList<T>'sConvertAllメソッドを使用することです。

var names = buisnessNames.ConvertAll(item => item.Name);

ただし、空でない可能性がある場合は、if ステートメントも使用する必要があります。

var names = new List<string>();
//some code
if (names.Any())
    names.AddRange(/*your selected method*/);
else
    names = //your selected method;
于 2013-08-21T21:09:52.513 に答える
1

businessNames はリストであるため、List.ForEach を使用できます。

businessNames.ForEach(bn => names.Add(bn.Name));
于 2013-08-21T21:23:14.000 に答える
0
List<string> names = (from b in buisnessNames select b.Name).ToList();

またはそのようなもの...

于 2013-08-21T21:24:31.210 に答える