0

I am using LinkedList data structure serverList to store the elements in it. As of now, it can also insert null in the LinkedList serverList which is not what I want. Is there any other data structure which I can use which will not add null element in the serverList list but maintain the insert ordering?

    public List<String> getServerNames(ProcessData dataHolder) {
        // some code

        String localIP = getLocalIP(localPath, clientId);
        String localAddress = getLocalAddress(localPath, clientId);

        // some code

        List<String> serverList = new LinkedList<String>();

        serverList.add(localIP);
        if (ppFlag) {
            serverList.add(localAddress);
        }
        if (etrFlag) {
            for (String remotePath : holderPath) {
                String remoteIP = getRemoteIP(remotePath, clientId);
                String remoteAddress = getRemoteAddress(remotePath, clientId);
                serverList.add(remoteIP);
                if (ppFlag) {
                    serverList.add(remoteAddress);
                }
            }
        }

        return serverList;
    }

This method will return a List which I am iterating it in a for loop in normal way. I can have empty serverList if everything is null, instead of having four null values in my list. In my above code, getLocalIP, getLocalAddress, getRemoteIP and getRemoteAddress can return null and then it will add null element in the linked list. I know I can add a if check but then I need to add if check four time just before adding to Linked List. Is there any better data structure which I can use here?

One constraint I have is - This library is use under very heavy load so this code has to be fast since it will be called multiple times.

john
  • 11,311
  • 40
  • 131
  • 251
  • 1
    possible duplicate of [Is there a Java Collection Framework class that doesn't allow null elements?](http://stackoverflow.com/questions/6997142/is-there-a-java-collection-framework-class-that-doesnt-allow-null-elements) – Tim Biegeleisen Jul 07 '15 at 07:28
  • 1
    Have you read this: https://code.google.com/p/guava-libraries/wiki/LivingWithNullHostileCollections ? – Tom Jul 07 '15 at 07:29
  • 1
    What is wrong with putting a check for `null` in your code? From what I can see, you would only need to do this comparison _once_ before the insertion. – Tim Biegeleisen Jul 07 '15 at 07:29
  • 1
    @TimBiegeleisen There is nothing wrong. I feel it will look ugly if I do that. May be I am wrong. – john Jul 07 '15 at 07:31
  • @Tom No I haven't. Which one will be suited to my needs here? Any thoughts? – john Jul 07 '15 at 07:31
  • @david Would there be anything wrong with using a `Set`? Then you can add `null` multiple times and then remove the value before you use it? – Tim Biegeleisen Jul 07 '15 at 07:35
  • This depends on the scenario and since you know it better than we do, you decide that yourself. That link contains some collections which prohibit `null`, so you only need to find pros and con of them and then decide which one suits you best. – Tom Jul 07 '15 at 07:36

6 Answers6

3

I am using LinkedList data structure serverList to store the elements in it.

That's most probably wrong, given that you're aiming at speed. An ArrayList is much faster unless you're using it as a Queue or alike.

I know I can add a if check but then I need to add if check four time just before adding to Linked List. Is there any better data structure which I can use here?

A collection silently ignoring nulls would be a bad idea. It may be useful sometimes and very surprising at other times. Moreover, it'd violate the List.add contract. So you won't find it in any serious library and you shouldn't implement it.


Just write a method

void <E> addIfNotNullTo(Collection<E> collection, E e) {
     if (e != null) {
         collection.add(e);
     }
}

and use it. It won't make your code really shorter, but it'll make it clearer.


One constraint I have is - This library is use under very heavy load so this code has to be fast since it will be called multiple times.

Note that any IO is many orders of magnitude slower than simple list operations.

maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • I am using LinkedList since I need to have ordering, ArrayList doesn't guarantee ordering. Do you see any other data structure which I can use which will not add null elements? My current method returns list and then I am iterating it in normal way in for loop. – john Jul 07 '15 at 17:48
  • Also I found this [article](http://www.leveluplunch.com/java/examples/remove-filter-null-references-from-collection-list/), not sure which one is better here. I can test it though but if you have any thoughts, let me know as well on this. – john Jul 07 '15 at 17:51
  • 2
    @david *"ArrayList doesn't guarantee ordering"* WAT??? That's non-sense. You're confusing things, possibly with `LinkedHashMap` vs `HashMap`. +++ Adding and removing nulls is surely slower than not adding them at all, but as I wrote, the speed of this part most probably doesn't matter. It feels a bit strange, but you can do it. – maaartinus Jul 07 '15 at 19:01
  • Yes you are right, I was confusing this with HashMap vs LinkedHashMap. Thanks for pointing that out. – john Jul 07 '15 at 22:21
  • So should I use as you suggested your method to avoid adding null or any of the guava stuff I can use here as well? – john Jul 07 '15 at 22:42
  • @david I'd go for my method, but another solution may be fine as well. Whatever suits you. Just keep it simple and avoid over-clever surprising solutions. – maaartinus Jul 07 '15 at 23:12
0

Use Apache Commons Collection:

ListUtils.predicatedList(new ArrayList(), PredicateUtils.notNullPredicate());

Adding null to this list throws IllegalArgumentException. Furthermore you can back it by any List implementation you like and if necessary you can add more Predicates to be checked.

Same exists for Collections in general.

Raman Shrivastava
  • 2,923
  • 15
  • 26
0

There are data structures that do not allow null elements, such as ArrayDeque, but these will throw an exception rather than silently ignore a null element, so you'd have to check for null before insertion anyway.

If you're dead set against adding null checks before insertion, you could instead iterate over the list and remove null elements before you return it.

ryanm
  • 2,979
  • 18
  • 22
0

The simplest way would be to just override LinkedList#add() in your getServerNames() method.

List<String> serverList = new LinkedList<String>() {
    public boolean add(String item) {
        if (item != null) {
          super.add(item);
          return true;
        } else
          return false;
    }
};

serverList.add(null);
serverList.add("NotNULL");
System.out.println(serverList.size()); // prints 1

If you then see yourself using this at several places, you can probably turn it into a class.

Ravi K Thapliyal
  • 51,095
  • 9
  • 76
  • 89
  • 3
    This is not bullet proof - For example: will that also work with addAll? – assylias Jul 07 '15 at 07:42
  • *"If you then see yourself using this at several places, you can probably turn it into a class"* Well, I would recommend to do that even if that sub-type is only used in one place to give that class a name which clearly shows that `null` is ignored by that list implementation. This fact can be missed if one uses an anonymous subtype. – Tom Jul 07 '15 at 07:44
  • I'm getting this impression that the issue is local to `getServerNames()`, the implementation of which is totally under OP's control. I think he just wants to avoid repeating `null` checks. – Ravi K Thapliyal Jul 07 '15 at 07:45
  • 4
    This actually violates the `List` contract as [List.add](https://docs.oracle.com/javase/7/docs/api/java/util/List.html#add%28E%29) *must* return true and must throw an Exception (NPE or IAE) if the element is unsuitable for the list. – Tagir Valeev Jul 07 '15 at 07:49
  • No, the add() must return true **if** the list is modified, false otherwise. – Ravi K Thapliyal Jul 07 '15 at 07:52
  • @RaviThapliyal - That is incorrect. The javadoc states: *"If a collection refuses to add a particular element for any reason other than that it already contains the element, **it must throw an exception** (rather than returning false)."* – Stephen C Jul 07 '15 at 08:00
  • @StephenC I checked the docs again and it says `as specified by Collection.add()` which says `returns true if this collection changed as a result of the call`. I think I'm safe to assume that if it doesn't it should return false. – Ravi K Thapliyal Jul 07 '15 at 08:03
  • Anyway, the gist of my answer here is to just avoid getting dependent on, say a Guava jar, just because one of the functions required a non-null only List. I understand @assylias point that I can't send this List floating around my code as freely as a robust non-null collection a third-party library would provide. – Ravi K Thapliyal Jul 07 '15 at 08:07
  • @RaviThapliyal The doc says "returns `true` (as specified by ...)". Meaning that if `List.add` returns instead of throwing an exception, the return value **must** be `true`. – Olivier Grégoire Jul 07 '15 at 10:49
  • @OlivierGrégoire and the premise behind that is *to preserve the invariant that a collection always contains the specified element after this call returns* but because the requirement is to not have the element null at all, it does not apply. The overriding method or the class is always welcome and it should document its behavior clearly. A null-check utility method on the other hand is no better because of the point already raised by assylias --- it does not guarantee that the collection will stay that way. – Ravi K Thapliyal Jul 07 '15 at 20:43
  • @RaviThapliyal an overriding method can refine the definition, not change it. List forces the value to be returned as `true`. If you want to support the return value `false`, don't implement a list, even if you document it. Create a `NonNullElementsList` interface which is a copy/paste of `List` bar the `add` speciality, if you want, but don't use `List`. If you want a `List` and don't want a `null` element, throwing an exception is your only choice. There are issues with the API, I know, but we all have to deal with it. – Olivier Grégoire Jul 07 '15 at 21:05
0

You can use a plain Java HashSet to store your paths. The null value may be added multiple times, but it will only ever appears once in the Set. You can remove null from the Set and then convert to an ArrayList before returning.

Set<String> serverSet = new HashSet<String>();

    serverSet.add(localIP);
    if (ppFlag) {
        serverSet.add(localAddress);
    }
    if (etrFlag) {
        for (String remotePath : holderPath) {
            String remoteIP = getRemoteIP(remotePath, clientId);
            String remoteAddress = getRemoteAddress(remotePath, clientId);
            serverSet.add(remoteIP);
            if (ppFlag) {
                serverSet.add(remoteAddress);
            }
        }
    }

serverSet.remove(null);     // remove null from your set - no exception if null not present
List<String> serverList = new ArrayList<String>(serverSet);

return serverList;
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • this will work but I forgot to mention this code is being used under a library which has to be fast so we are copying an array from a set which might be expensive. – john Jul 07 '15 at 07:40
  • Then your best bet is probably to just do the explicit check before insertion. Despite the aesthetic problem, it should not affect your performance so much. – Tim Biegeleisen Jul 07 '15 at 07:42
  • Why do you need to return a `List` ? – Tim Biegeleisen Jul 07 '15 at 07:42
  • I was using LinkedList since I need ordering, do you see any other data structure which I can use? – john Jul 07 '15 at 17:52
0

Since you use Guava (it's tagged), I have this alternative if you have the luxury of being able to return a Collection instead of a List.

Why Collection ? Because List forces you to either return true or throw an exception. Collection allows you to return false if you didn't add anything to it.

class MyVeryOwnList<T> extends ForwardingCollection<T> { // Note: not ForwardingList
  private final List<T> delegate = new LinkedList<>(); // Keep a linked list
  @Override protected Collection<T> delegate() { return delegate; }

  @Override public boolean add(T element) {
    if (element == null) {
      return false;
    } else {
      return delegate.add(element);
    }
  }

  @Override public boolean addAll(Collection<? extends T> elements) {
    return standardAddAll(elements);
  }
}
Olivier Grégoire
  • 33,839
  • 23
  • 96
  • 137