What type of array do i need for each of these situations? And why are array lists "obsoleted"

Hi all. I’m poresently looking at this page; http://wiki.unity3d.com/index.php?title=Which_Kind_Of_Array_Or_Collection_Should_I_Use?

Firstly, i note with concern, this:

So i’ve searched through the other collection types there, and i’m just thinking wtf? They’re clearly not obsolete at all. Generic Lists are strictly typed, and are therefore NOT a valid replacement. I’m concerned at this attitude, especially the implication that UT only bbrought them in as a legacy compatibility thing. How else are you supposed to do mixed lists in C#?

Okay, anyways, my question.

I have need for two different arrays.

  1. An array where performance is not important. Values will be read from it once, and cached. This array must be two dimensional, and must contain both floats and strings. All the values in it will be filled manually at authortime so it will be a set size.

  2. An array where performance is utterly critical, it will be used and processed constantly. Must also be two dimensional. each record needs to contain exactly one reference to an object, and one integer. nothing else is needed.

The second one is really the most critical. I’ll be recalculating the value of that integer in each record frequently, and sorting the list in descending order of that integer (maybe once per second, generally as often as i can afford to do it). The list may contain hundreds, maybe even a few thousand elements at a time, and will be dynamic in size.

Since both of these are mixed lists, i’m given to understand that i’d need an ArrayList for each of them. Which lends farther disbelief to the statement that they are obsolete. But since that IS stated, i’ve got to wonder if i’m missing something here, hence that is the purpose for this thread.

I think i’ve stated my requirements for the second list pretty well, i’d like any advice on optimisation tricks to make that sort of data structure work well.

Use a generic List of type object. You will have to cast to the correct type when retrieving values, but that’s true of ArrayList too. There is literally zero use for ArrayList, and yes it’s totally obsolete; it’s only there so old code wouldn’t break.

As for the questions:

  1. 2D array.
  2. 1D array, where basic math is used to simulate a 2D array. Allocate the max size, and use an index to determine where the “end” is.

I doubt you really need to use an array of mixed types; that’s pretty rare. Use a custom class instead, and the arrays will use that class as the type.

–Eric

3 Likes

When you say mixed lists, you mean a list that has two types in it?

If that’s the case, why not just use a class that holds these two types and flatten the arrays to 1 dimension and store that type instead? You can even make a custom sorting object to sort the type, or simply make the type inherit from IComparable and define the CompareTo method for the type?

Also, the sorting done on List types should be in O(NlogN) for large amounts of data, which is pretty much the fastest running time a sort can be unless you’re sorting extremely specialized values based on their values and using a dynamic method.

I think

Do you mean a sort of jumping list, where i store reference,int, reference, int, reference, int, etc. And then increment the index by 2 instead of 1 each time? I’m thinking that would be complex to sort.

This sounds interesting and efficient. i’ll try to find more information, anything you could point me to?

I honestly have no idea what this means, a little over my head, but i’ll attempt to take it and learn. if you feel like simplifying that a bit, i wouldn’t object.

No, just an index that says where the end is. So if you had an array of 500 elements, but were only using 150, then the end index is 150. But as discussed above, it’s unlikely it should contain mixed elements. Use a custom class.

Actually, on second thought, just use a generic List; that would make things easier. An array is faster than a List, but wait until you’re done and profile to make sure that’s actually really a bottleneck that would be improved by using an array instead of a List.

–Eric

1 Like

Oh another question;

I would like, if possible, for my first list (the one where performance doesn’t matter) to be a constant/static variable, so that i don’t have to instance the class and can read it from anywhere. As mentioned all the data in it will just be entered manually at authortime, and it will only be read from, not written to. It’s really just a data storage

best way to accomplish that?

is it still possible to do with the custom class idea?

You can use a static variable, yes.

–Eric

Forgive me for this toy example. I’m not completely used to C# Generics so I wasn’t completely sure how to make a Generic version of say MixedType<T, U> where T contains the interface IComparable and U contains the interface IComparable

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

public class __TestScript300 : MonoBehaviour
{

    public int itemCount = 15;
    List<MixedData> myMixedData;

    class MixedData : IComparable<MixedData>
    {
        public string someString;
        public int someInt;

        public MixedData(string someString, int someInt)
        {
            this.someInt = someInt;
            this.someString = someString;
        }

        public int CompareTo(MixedData other)
        {
            return someInt.CompareTo(other.someInt);
        }

        public override string ToString()
        {
            return someInt + ": " + someString;
        }
    }

    class SortMixedDataByString : IComparer<MixedData>
    {
        public int Compare(MixedData left, MixedData right)
        {
            return left.someString.CompareTo(right.someString);
        }
    }


    void Start()
    {

        myMixedData = new List<MixedData>(itemCount);

        Debug.Log("-------------\nBefore sorting our custom type...");

        for (int i = 0; i < itemCount; i++)
        {
            float _a = UnityEngine.Random.Range(101f, 400f);
            float _b = UnityEngine.Random.Range(101f, 400f);
            float _c = UnityEngine.Random.Range(101f, 400f);
            myMixedData.Add(new MixedData("<" +_a + ", " + _b + ", " + _c + ">", (int)UnityEngine.Random.Range(1f, 100f)));
            Debug.Log(myMixedData[i]);
        }

        myMixedData.Sort();

        Debug.Log("-------------\nAfter sorting our custom type based on its default sorting method...");

        for (int i = 0; i < myMixedData.Count; i++)
            Debug.Log(myMixedData[i]);

        myMixedData.Sort(new SortMixedDataByString());

        Debug.Log("-------------\nAfter sorting our custom type based on its other parameter");

        for (int i = 0; i < myMixedData.Count; i++)
            Debug.Log(myMixedData[i]);
    }

}

The custom class inherits from IComparable so we can properly compare against our custom type when performing a List.Sort operation. If the custom type doesn’t inherit from IComparable, I don’t know if C# will sort the values (I doubt it, but I could be wrong). By explicitly defining how the sort should be done, you have more control over the ordering of your custom class.

The other method of sorting in this scenario is with an object instance that inherits from ICompararer. This method allows you to override the default sorting behavior for your custom type, giving you even more options.

Edit: It doesn’t look like your custom type needs to inherit from IComparable at all, as long as you make an IComparer for your type and supply it to the List at the call of List.Sort

using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;

public class __TestScript301 : MonoBehaviour
{

    public int itemCount = 15;
    List<DoesntInheritIComparable> otherList;

    class DoesntInheritIComparable
    {

        public string someString;
        public int someInt;

        public DoesntInheritIComparable(string someString, int someInt)
        {
            this.someInt = someInt;
            this.someString = someString;
        }

        public override string ToString()
        {
            return someInt + ": " + someString;
        }
    }

    class SortOtherDataByInt : IComparer<DoesntInheritIComparable>
    {
        public int Compare(DoesntInheritIComparable left, DoesntInheritIComparable right)
        {
            return left.someInt.CompareTo(right.someInt);
        }
    }


    void Start()
    {

        otherList = new List<DoesntInheritIComparable>();

        Debug.Log("-------------\nBefore sorting our custom type...");

        for (int i = 0; i < itemCount; i++)
        {
            float _a = UnityEngine.Random.Range(101f, 400f);
            float _b = UnityEngine.Random.Range(101f, 400f);
            float _c = UnityEngine.Random.Range(101f, 400f);
            otherList.Add(new DoesntInheritIComparable("<" + _a + ", " + _b + ", " + _c + ">", (int)UnityEngine.Random.Range(1f, 100f)));
            Debug.Log(otherList[i]);
        }

        Debug.Log("-----------\nSorting our type that doesn't inherit from IComparable by using an IComparer");

        otherList.Sort(new SortOtherDataByInt());

        for (int i = 0; i < otherList.Count; i++)
            Debug.Log(otherList[i]);
    }

}

If you’re never planning on sorting by more than one type (for example, you only plan to sort by integers) then you only need one class that follows the IComparer interface for your custom type.

1 Like

Consider a Dictionary too for seek performance.

regarding my first requirement, the non intensive data list;

Here’s what i’m attempting to do

using UnityEngine;
using System.Collections;

public class PMats : MonoBehaviour
{
    public const PMaterialData[] data = {new PMaterialData("Steel", 7.75f, 671f, 240f)};
}

this refuses to compile with the error: Assets/Scripts/Dictionaries/Static/PMats.cs(7,34): error CS0134: A constant PMats.data' of reference type PMaterialData[ ]’ can only be initialized with null

Here’s the PMaterialData class by the way

using UnityEngine;
using System.Collections;

public class PMaterialData : MonoBehaviour
{
    public string physicMaterialName;//The name of the physic material to which this data corresponds
    public float density;//Expressed as Kilograms per Cubic Metre (Kg/m^3)
    public float tensileStrength;//Expressed as pascals required to break a chunk off an object
    public float compressiveStrength;//Expressed as Pascals required to fracture an object and make it "give*

    public PMaterialData(string name, float den, float ten, float comp)
    {
        physicMaterialName = name;
        density = den;
        tensileStrength = ten;
        compressiveStrength = comp;
    }
}

what am i doing wrong?

That shouldn’t be using const. Const means constant; i.e. no change. You would use that for things that are set once and never again, like “const int meaningOfLife = 42;”.

–Eric

I don’t know what the rules with const in C# are, but more importantly, your Monobehaviour has a Constructor which isn’t good. You should always do initialization of a Monobehaviour in the Start method.

Do you want to generate your PMaterialData dynamically? If so, you should consider using a new GameObject, set its position, then add a PMaterialData component to it, assigning the values to the component after applying it to the GameObject. The downside to doing this is that you’ll have to manually set the size of the PMaterialData[ ] array in PMats and also manage which index you need to assign a new reference of a PMaterialData to the PMaterialData[ ] array in PMats. You can probably make a helper method in PMats to do that for you, though so it’s not really that bad.

Otherwise you can setup your Scene and assign each GameObject’s PMaterialData individually. There’s a pretty big benefit of doing things this way, as you don’t have to instantiate the PMaterialData array with a size and you can simply assign references of each GameObject’s PMaterialData directly to the PMats data array [via the Inspector]

Right, I didn’t see the MonoBehaviour; that should likely be removed since I can’t see any reason for the PMaterialData class to extend MonoBehaviour.

–Eric

1 Like

I know what constant means. that IS EXACTLY what i want.

This is data i’m entering at authortime. I want the variables to be tied to the class, not an instance of the class, so that i can read them without instancing it. i want to never ever have to instance PMats at all.I’ll read data from it something like this:

density = PMats.data[PMats.STEEL].density;

PMats.STEEL will be from a set of integers corresponding to the appropriate array index

But PMats isn’t going to be called, so how can i be running any methods? i’m a bit confused as to how this works

No, nothing dynamic. This is entirely just a storage method for data entered at authortime

This method honestly seems cumbersome. wouldn’t that make it annoyingly difficult to view/edit the material parameters? I’d really rather have them all in plaintext, in the PMats file, like the sample record shown above for steel. i’ll keep this in mind as an alternate/last resort.

No, you want static, not constant.

–Eric

don’t i want both? constant means it can’t change at runtime doesn’t it?, and that is what i want

Just use static. As far as I know you can’t use const with arrays, since they are initialized at runtime, and const is only for compile-time variables.

–Eric

const only works with numbers, string, bool, and null. No classes, dates, structs, etc… are allowed to be const by the C# compiler. Arrays are classes, even if it’s an array of bool.

If you want “const” type behavior from a class, use static readonly. Though it does have some subtle differences.